Project_WL/AgentScripts/WL816r_Demo.cs

255 lines
14 KiB
C#
Raw Normal View History

// WL-816r — 데모 맵의 「흙(밭·길)」 화면색·질감 실측. 에디트 모드 전용(데모 Play 금지).
// 원본 무수정: 씬을 열기만 하고 저장하지 않는다. 프로브 오브젝트는 렌더 후 즉시 파괴.
public static class WL816r_Demo
{
const int W = 1024, H = 1024;
static System.Text.StringBuilder sb;
// 우리 텍스처의 현재 세 사분면 (sRGB) — 프로브 기준점
static readonly string[] BaseHex = { "A0E4A2", "CCCBB5", "807057" };
static readonly string[] BaseTag = { "우리초록(무변경)", "우리모래", "우리흙" };
public static void Run()
{
sb = new System.Text.StringBuilder();
var sc = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/3DPixelArtEnvironment/Demo/Demo.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
L("=== Demo.unity roots=" + sc.rootCount + " ===");
var terrain = UnityEngine.Object.FindFirstObjectByType<UnityEngine.Terrain>();
if (terrain == null) { L("NO TERRAIN"); Dump(); return; }
var td = terrain.terrainData;
var layers = td.terrainLayers;
for (int i = 0; i < layers.Length; i++) L("layer[" + i + "] = " + (layers[i] ? layers[i].name : "-"));
int grassIdx = 0, dirtIdx = 1;
for (int i = 0; i < layers.Length; i++)
{
if (layers[i] == null) continue;
if (layers[i].name.ToLower().Contains("dirt")) dirtIdx = i;
if (layers[i].name.ToLower().Contains("grass")) grassIdx = i;
}
L("grassIdx=" + grassIdx + " dirtIdx=" + dirtIdx + " · terrain size=" + td.size + " pos=" + terrain.transform.position);
// ── 스플랫맵에서 흙 100 % / 풀 100 % 인 월드 좌표를 고른다 ─────────────
int ar = td.alphamapResolution;
var a = td.GetAlphamaps(0, 0, ar, ar);
var dirtPts = new System.Collections.Generic.List<UnityEngine.Vector3>();
var grassPts = new System.Collections.Generic.List<UnityEngine.Vector3>();
int nd = 0, ng = 0;
for (int y = 0; y < ar; y += 2)
for (int x = 0; x < ar; x += 2)
{
float wd = a[y, x, dirtIdx], wg = a[y, x, grassIdx];
if (wd > 0.98f) nd++;
if (wg > 0.98f) ng++;
}
L("알파맵 res=" + ar + " · 흙 100% 표본=" + nd + " · 풀 100% 표본=" + ng);
// 평탄도까지 본다(경사면은 램프 단이 달라 비교가 흐려진다)
System.Func<float, float, float> hAt = (u, v) => terrain.transform.position.y + td.GetInterpolatedHeight(u, v);
System.Func<float, float, float> slope = (u, v) =>
{
var n = td.GetInterpolatedNormal(u, v);
return UnityEngine.Vector3.Angle(n, UnityEngine.Vector3.up);
};
for (int y = 0; y < ar; y += 1)
for (int x = 0; x < ar; x += 1)
{
float u = (float)x / (ar - 1), v = (float)y / (ar - 1);
float wd = a[y, x, dirtIdx], wg = a[y, x, grassIdx];
if (slope(u, v) > 6f) continue;
var p = new UnityEngine.Vector3(terrain.transform.position.x + u * td.size.x, hAt(u, v),
terrain.transform.position.z + v * td.size.z);
if (wd > 0.99f && dirtPts.Count < 4000) dirtPts.Add(p);
else if (wg > 0.99f && grassPts.Count < 4000) grassPts.Add(p);
}
L("평탄(≤6°) 흙점=" + dirtPts.Count + " · 풀점=" + grassPts.Count);
if (dirtPts.Count == 0) { L("평탄 흙점 0 — 중단"); Dump(); return; }
// 흙점들의 무게중심 근처를 찍는다
var c = UnityEngine.Vector3.zero;
foreach (var p in dirtPts) c += p;
c /= dirtPts.Count;
L("흙 무게중심=" + c);
// ── 프로브 판 (우리 Toon 머티리얼 · 후보색) ────────────────────────────
var topMat = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(
"Assets/WL/Look/Farm/Materials/Farm_IslandTop_Arena.mat");
var shadowRatio = new UnityEngine.Color(0.3248f, 0.3416f, 0.3936f, 1f);
if (topMat != null)
{
var d0 = topMat.GetColor("_DiffuseColor"); var s0 = topMat.GetColor("_ShadowDiffuseColor");
L("Farm_IslandTop_Arena: Dif=" + Cs(d0) + " Shd=" + Cs(s0) + " Shades=" + topMat.GetFloat("_Shades")
+ " Bright=" + topMat.GetFloat("_Brightness") + " MinDark=" + topMat.GetFloat("_MinimumDarkness")
+ " kw=[" + string.Join(",", topMat.shaderKeywords) + "]");
shadowRatio = new UnityEngine.Color(s0.r, s0.g, s0.b, 1f);
}
var roadMat = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(
"Assets/WL/Look/Farm/Materials/Farm_Road01_ToonTex.mat");
if (roadMat != null)
L("Farm_Road01_ToonTex: Dif=" + Cs(roadMat.GetColor("_DiffuseColor")) + " Shd=" + Cs(roadMat.GetColor("_ShadowDiffuseColor"))
+ " kw=[" + string.Join(",", roadMat.shaderKeywords) + "]");
var probeRoot = new UnityEngine.GameObject("~816rProbe");
var mats = new System.Collections.Generic.List<UnityEngine.Material>();
var probes = new System.Collections.Generic.List<UnityEngine.Vector3>();
var probeTag = new System.Collections.Generic.List<string>();
float step = 2.2f;
for (int i = 0; i < BaseHex.Length; i++)
{
var srgb = Hex(BaseHex[i]);
var lin = srgb.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 * shadowRatio.r, lin.g * shadowRatio.g, lin.b * shadowRatio.b, 1f));
mats.Add(m);
var q = UnityEngine.GameObject.CreatePrimitive(UnityEngine.PrimitiveType.Quad);
q.transform.SetParent(probeRoot.transform);
q.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f);
q.transform.localScale = new UnityEngine.Vector3(2f, 2f, 1f);
var pos = c + new UnityEngine.Vector3((i - 1) * step, 0.35f, 4.5f);
q.transform.position = pos;
q.GetComponent<UnityEngine.MeshRenderer>().sharedMaterial = m;
UnityEngine.Object.DestroyImmediate(q.GetComponent<UnityEngine.Collider>());
probes.Add(pos); probeTag.Add(BaseTag[i] + " #" + BaseHex[i]);
}
// ── 카메라: 흙 무게중심 위 탑다운 직교 ─────────────────────────────────
var camGo = new UnityEngine.GameObject("~816rCam");
var cam = camGo.AddComponent<UnityEngine.Camera>();
cam.orthographic = true; cam.orthographicSize = 8f;
cam.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f);
cam.transform.position = new UnityEngine.Vector3(c.x, c.y + 40f, c.z + 2.2f);
cam.clearFlags = UnityEngine.CameraClearFlags.SolidColor;
cam.backgroundColor = new UnityEngine.Color(1f, 0f, 1f, 1f);
cam.nearClipPlane = 0.1f; cam.farClipPlane = 200f;
var tex = Grab(cam, W, H);
System.IO.Directory.CreateDirectory("Screenshots_WL/WL816r");
System.IO.File.WriteAllBytes("Screenshots_WL/WL816r/demo_dirt_probe.png", UnityEngine.ImageConversion.EncodeToPNG(tex));
var px = tex.GetPixels32();
L("--- 데모 지형 화면색(탑다운 ortho8 · 같은 렌더) ---");
L(" 흙 " + SampleSet(cam, tex, px, dirtPts, 600));
L(" 풀 " + SampleSet(cam, tex, px, grassPts, 600));
for (int i = 0; i < probes.Count; i++)
L(" 프로브 " + probeTag[i] + " " + SamplePatch(cam, tex, px, probes[i], 14));
// ── 질감: 흙 구역 안의 색 분포 ─────────────────────────────────────────
L("--- 흙 질감(같은 렌더 · 흙 표본 화면픽셀) ---");
L(" 흙 고유색/에지 " + Texture(cam, tex, px, dirtPts));
L(" 풀 고유색/에지 " + Texture(cam, tex, px, grassPts));
// ── 데모 안의 「길」 후보 오브젝트 ─────────────────────────────────────
L("--- 데모 렌더러 머티리얼 분포 ---");
var cnt = new System.Collections.Generic.Dictionary<string, int>();
foreach (var r in UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsSortMode.None))
foreach (var m in r.sharedMaterials)
{
if (m == null) continue;
string k = m.name;
cnt[k] = cnt.ContainsKey(k) ? cnt[k] + 1 : 1;
}
foreach (var kv in cnt) L(" " + kv.Key + " × " + kv.Value);
UnityEngine.Object.DestroyImmediate(tex);
UnityEngine.Object.DestroyImmediate(camGo);
UnityEngine.Object.DestroyImmediate(probeRoot);
foreach (var m in mats) UnityEngine.Object.DestroyImmediate(m);
Dump();
}
static UnityEngine.Color Hex(string h)
{
int r = System.Convert.ToInt32(h.Substring(0, 2), 16);
int g = System.Convert.ToInt32(h.Substring(2, 2), 16);
int b = System.Convert.ToInt32(h.Substring(4, 2), 16);
return new UnityEngine.Color(r / 255f, g / 255f, b / 255f, 1f);
}
static string Cs(UnityEngine.Color c) { return "(" + c.r.ToString("F4") + "," + c.g.ToString("F4") + "," + c.b.ToString("F4") + ")"; }
static string SampleSet(UnityEngine.Camera cam, UnityEngine.Texture2D tex, UnityEngine.Color32[] px,
System.Collections.Generic.List<UnityEngine.Vector3> pts, int maxN)
{
long sr = 0, sg = 0, sb2 = 0; int n = 0;
int stepN = UnityEngine.Mathf.Max(1, pts.Count / maxN);
for (int i = 0; i < pts.Count; i += stepN)
{
var s = cam.WorldToScreenPoint(pts[i]);
int x = (int)s.x, y = (int)s.y;
if (x < 2 || y < 2 || x >= W - 2 || y >= H - 2) continue;
var p = px[y * W + x];
if (p.r > 200 && p.g < 60 && p.b > 200) continue;
sr += p.r; sg += p.g; sb2 += p.b; n++;
}
if (n == 0) return "표본 0";
return string.Format("n={0} 평균=#{1:X2}{2:X2}{3:X2} ({4},{5},{6})", n, sr / n, sg / n, sb2 / n, sr / n, sg / n, sb2 / n);
}
static string SamplePatch(UnityEngine.Camera cam, UnityEngine.Texture2D tex, UnityEngine.Color32[] px, UnityEngine.Vector3 w, int rad)
{
var s = cam.WorldToScreenPoint(w);
int cx = (int)s.x, cy = (int)s.y;
long sr = 0, sg = 0, sb2 = 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 (p.r > 200 && p.g < 60 && p.b > 200) continue;
sr += p.r; sg += p.g; sb2 += p.b; n++;
}
if (n == 0) return "표본 0";
return string.Format("n={0} 평균=#{1:X2}{2:X2}{3:X2} ({4},{5},{6})", n, sr / n, sg / n, sb2 / n, sr / n, sg / n, sb2 / n);
}
static string Texture(UnityEngine.Camera cam, UnityEngine.Texture2D tex, UnityEngine.Color32[] px,
System.Collections.Generic.List<UnityEngine.Vector3> pts)
{
var uniq = new System.Collections.Generic.Dictionary<int, int>();
int edge = 0, cmp = 0; int n = 0;
int stepN = UnityEngine.Mathf.Max(1, pts.Count / 1500);
for (int i = 0; i < pts.Count; i += stepN)
{
var s = cam.WorldToScreenPoint(pts[i]);
int x = (int)s.x, y = (int)s.y;
if (x < 2 || y < 2 || x >= W - 2 || y >= H - 2) continue;
var p = px[y * W + x];
if (p.r > 200 && p.g < 60 && p.b > 200) continue;
int key = (p.r << 16) | (p.g << 8) | p.b;
uniq[key] = uniq.ContainsKey(key) ? uniq[key] + 1 : 1;
var q = px[y * W + x + 1];
cmp++; n++;
if (System.Math.Abs(p.r - q.r) + System.Math.Abs(p.g - q.g) + System.Math.Abs(p.b - q.b) > 6) edge++;
}
// 상위 5색
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(5, list.Count); i++)
top.Append(string.Format(" #{0:X6}×{1}", list[i].Key, list[i].Value));
return string.Format("n={0} 고유색={1} 에지%={2:F2} 상위5:{3}", 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); }
static void Dump()
{
System.IO.File.WriteAllText("AgentScripts/WL816r_DEMO.txt", sb.ToString());
UnityEngine.Debug.Log("[816r demo]\n" + sb.ToString());
}
}