Project_WL/AgentScripts/WL816r_Demo.cs

255 lines
14 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 — 데모 맵의 「흙(밭·길)」 화면색·질감 실측. 에디트 모드 전용(데모 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());
}
}