Project_WL/AgentScripts/WL816n_Demo.cs

133 lines
7.1 KiB
C#
Raw Normal View History

// WL-816n — 데모 풀밭의 「색 경계」가 무엇으로 생기는지 격리 렌더로 가른다. 에디트 모드 전용(데모 Play 금지).
// 원본 에셋 무수정: terrain.materialTemplate 에 복사본을 잠시 물렸다가 원복하고, 씬은 저장하지 않는다.
public static class WL816n_Demo
{
const int W = 540, H = 960;
public static void Run()
{
var sb = new System.Text.StringBuilder();
var sc = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/3DPixelArtEnvironment/Demo/Demo.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
sb.AppendLine("=== Demo.unity roots=" + sc.rootCount + " ===");
var terrain = UnityEngine.Object.FindFirstObjectByType<UnityEngine.Terrain>();
if (terrain == null) { sb.AppendLine("NO TERRAIN"); Dump(sb); return; }
var td = terrain.terrainData;
sb.AppendLine("terrain size=" + td.size + " pos=" + terrain.transform.position
+ " mat=" + (terrain.materialTemplate ? terrain.materialTemplate.name : "null")
+ " alphamapTex=" + td.alphamapTextureCount + " alphamapRes=" + td.alphamapResolution
+ " heightmapRes=" + td.heightmapResolution + " detailProtos=" + td.detailPrototypes.Length);
// 높이 분포(= 툰 램프 띠의 원천 후보)
float hmin = 1e9f, hmax = -1e9f;
for (int i = 0; i <= 32; i++)
for (int j = 0; j <= 32; j++)
{ var h = td.GetInterpolatedHeight(i / 32f, j / 32f); if (h < hmin) hmin = h; if (h > hmax) hmax = h; }
sb.AppendLine("terrain height min=" + hmin.ToString("F2") + " max=" + hmax.ToString("F2") + " (월드 y 범위)");
// 데모 풀 배치 규칙 (ⓓ)
var tib = terrain.GetComponent<Environment.Instancing.TerrainInstancesBehaviour>();
if (tib == null) tib = UnityEngine.Object.FindFirstObjectByType<Environment.Instancing.TerrainInstancesBehaviour>();
if (tib != null)
{
sb.AppendLine("--- TerrainInstancesBehaviour (배치 규칙) ---");
sb.AppendLine(" PositionVariance=" + tib.PositionVariance + " ScaleVariance=" + tib.ScaleVariance);
DumpLayer(sb, "FirstLayer", tib.FirstLayer);
DumpLayer(sb, "SecondLayer", tib.SecondLayer);
DumpLayer(sb, "ThirdLayer", tib.ThirdLayer);
DumpLayer(sb, "FourthLayer", tib.FourthLayer);
}
else sb.AppendLine("TerrainInstancesBehaviour 없음");
// 카메라 (816f 판정 카메라와 같은 각 · 직교 6)
var camGo = new UnityEngine.GameObject("~816nCam");
var cam = camGo.AddComponent<UnityEngine.Camera>();
cam.orthographic = true; cam.orthographicSize = 6f;
cam.transform.rotation = UnityEngine.Quaternion.Euler(30f, 60f, 0f);
var c = terrain.transform.position + td.size * 0.5f; c.y = terrain.transform.position.y + hmax;
cam.transform.position = c - cam.transform.forward * 40f;
cam.clearFlags = UnityEngine.CameraClearFlags.SolidColor;
cam.backgroundColor = new UnityEngine.Color(1f, 0f, 1f, 1f);
cam.nearClipPlane = 0.1f; cam.farClipPlane = 200f;
var orig = terrain.materialTemplate;
var copy = new UnityEngine.Material(orig);
terrain.materialTemplate = copy;
sb.AppendLine("--- 격리 렌더 (데모 바닥 · 풀은 에디트 모드에서 안 그려짐) ---");
Shot(sb, cam, copy, "A_baseline", orig, false, -1);
Shot(sb, cam, copy, "B_clouds_off", orig, true, -1);
Shot(sb, cam, copy, "C_shades1", orig, false, 1);
Shot(sb, cam, copy, "D_clouds_off_shades1", orig, true, 1);
terrain.materialTemplate = orig;
UnityEngine.Object.DestroyImmediate(copy);
UnityEngine.Object.DestroyImmediate(camGo);
Dump(sb);
}
static void DumpLayer(System.Text.StringBuilder sb, string name, Environment.Instancing.TerrainInstancesBehaviour.TerrainInstancingInput v)
{
if (v == null) { sb.AppendLine(" " + name + " null"); return; }
sb.AppendLine(" " + name + " Density=" + v.Density + " settings=" + (v.Settings == null ? 0 : v.Settings.Length));
if (v.Settings == null) return;
foreach (var s in v.Settings)
sb.AppendLine(" mesh=" + (s.Mesh ? s.Mesh.name : "-") + " mat=" + (s.Material ? s.Material.name : "-")
+ " scale=" + s.Scale + " prob=" + s.Probability + " normalOffset=" + s.NormalOffset);
}
static void Shot(System.Text.StringBuilder sb, UnityEngine.Camera cam, UnityEngine.Material copy, string tag,
UnityEngine.Material orig, bool cloudsOff, int shades)
{
copy.CopyPropertiesFromMaterial(orig);
if (cloudsOff) { copy.SetFloat("_Cloud_Strength", 0f); copy.DisableKeyword("_CLOUDSENABLED"); }
if (shades > 0) copy.SetFloat("_Shades", shades);
var rt = new UnityEngine.RenderTexture(W, H, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
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;
var px = tex.GetPixels32();
// 배경(마젠타) 제외한 지형 픽셀만
var idx = new System.Collections.Generic.List<int>();
for (int i = 0; i < px.Length; i++)
if (!(px[i].r > 200 && px[i].g < 60 && px[i].b > 200)) idx.Add(i);
// 고유색 수 + 에지%(가로 인접 차 > 6)
var uniq = new System.Collections.Generic.HashSet<int>();
long sr = 0, sg = 0, sb2 = 0; int edge = 0, cmp = 0;
foreach (var i in idx)
{
uniq.Add((px[i].r << 16) | (px[i].g << 8) | px[i].b);
sr += px[i].r; sg += px[i].g; sb2 += px[i].b;
int x = i % W;
if (x + 1 < W)
{
int j = i + 1;
if (!(px[j].r > 200 && px[j].g < 60 && px[j].b > 200))
{
cmp++;
int d = System.Math.Abs(px[i].r - px[j].r) + System.Math.Abs(px[i].g - px[j].g) + System.Math.Abs(px[i].b - px[j].b);
if (d > 6) edge++;
}
}
}
int n = idx.Count == 0 ? 1 : idx.Count;
sb.AppendLine(string.Format(" {0,-22} 지형px={1} 평균=#{2:X2}{3:X2}{4:X2} 고유색={5} 색경계%={6:F2}",
tag, idx.Count, sr / n, sg / n, sb2 / n, uniq.Count, cmp == 0 ? 0f : 100f * edge / cmp));
System.IO.Directory.CreateDirectory("Screenshots_WL/WL816n");
System.IO.File.WriteAllBytes("Screenshots_WL/WL816n/demo_" + tag + ".png", UnityEngine.ImageConversion.EncodeToPNG(tex));
UnityEngine.Object.DestroyImmediate(tex);
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
}
static void Dump(System.Text.StringBuilder sb)
{
System.IO.File.WriteAllText("AgentScripts/WL816n_DEMO.txt", sb.ToString());
UnityEngine.Debug.Log("[816n] demo probe done\n" + sb.ToString());
}
}