Project_WL/AgentScripts/WL816a_Flat.cs

204 lines
9.8 KiB
C#
Raw Normal View History

// WL-816a — 단색안(데모처럼 텍스처 0) 머티리얼을 「메시·서브메시별 팔레트 평균색」으로 다시 만든다.
// WL_FarmLook 씬의 WLFarmLook.entries[].original 을 근거로 계산하고 entries[].toonFlat 에 다시 꽂는다.
public static class WL816a_Flat
{
const string DstScene = "Assets/WL/Look/Farm/Scenes/WL_FarmLook.unity";
const string FlatDir = "Assets/WL/Look/Farm/Materials/Flat";
static readonly UnityEngine.Color ShadowRatio = new UnityEngine.Color(0.2164f, 0.2092f, 0.3713f, 0f);
public static void Run()
{
var sb = new System.Text.StringBuilder();
var toon = UnityEngine.Shader.Find("Shader Graphs/Toon");
var scene = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
DstScene, UnityEditor.SceneManagement.OpenSceneMode.Single);
var comp = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLFarmLook>(UnityEngine.FindObjectsInactive.Include);
if (comp == null) { UnityEngine.Debug.Log("[WL816a Flat] WLFarmLook 없음"); return; }
var palette = LoadPng("Assets/FarmingIsland/Textures/Palette.png");
var island = LoadPng("Assets/FarmingIsland/Textures/Islands/Island01.png");
sb.AppendLine("팔레트 " + (palette != null ? palette.width + "x" + palette.height : "null")
+ " · 섬 " + (island != null ? island.width + "x" + island.height : "null"));
var cache = new System.Collections.Generic.Dictionary<string, UnityEngine.Material>();
int ok = 0, fail = 0, notex = 0, dbg = 0;
for (int i = 0; i < comp.entries.Length; i++)
{
var e = comp.entries[i];
if (e == null || e.target == null || e.original == null) continue;
UnityEngine.Mesh mesh = null;
var mf = e.target.GetComponent<UnityEngine.MeshFilter>();
if (mf != null) mesh = mf.sharedMesh;
var sk = e.target as UnityEngine.SkinnedMeshRenderer;
if (sk != null) mesh = sk.sharedMesh;
var flat = new UnityEngine.Material[e.original.Length];
for (int s = 0; s < e.original.Length; s++)
{
var src = e.original[s];
if (src == null || src.shader == null || src.shader.name != "Universal Render Pipeline/Simple Lit")
{ flat[s] = src; continue; }
var baseTex = src.HasProperty("_BaseMap") ? src.GetTexture("_BaseMap") : null;
var baseCol = src.HasProperty("_BaseColor") ? src.GetColor("_BaseColor") : UnityEngine.Color.white;
UnityEngine.Color col = baseCol;
string key = src.name;
if (dbg < 4)
{
sb.AppendLine(" dbg[" + dbg + "] rend=" + e.target.name + " src=" + src.name
+ " tex=" + (baseTex == null ? "null" : "'" + baseTex.name + "'")
+ " mesh=" + (mesh == null ? "null" : mesh.name));
dbg++;
}
UnityEngine.Texture2D lut = null;
if (baseTex != null)
{
if (baseTex.name == "Palette") lut = palette;
else if (baseTex.name == "Island01") lut = island;
}
if (lut == null) { notex++; }
else if (mesh == null) { col = MeanOf(lut); key = src.name + "_mean"; fail++; }
else
{
UnityEngine.Color c;
if (SampleMesh(mesh, s, lut, out c)) { col = c; key = src.name + "_" + mesh.name + "_" + s; ok++; }
else { col = MeanOf(lut); key = src.name + "_mean"; fail++; }
}
col.a = 0f;
flat[s] = Get(key, col, toon, cache);
}
e.toonFlat = flat;
}
UnityEditor.EditorUtility.SetDirty(comp);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(scene);
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(scene);
UnityEditor.AssetDatabase.SaveAssets();
sb.AppendLine("단색 머티리얼 " + cache.Count + "종 · UV 샘플 성공 " + ok + " · 실패 " + fail + " · 텍스처없음(원래단색) " + notex);
UnityEngine.Debug.Log("[WL816a Flat]\n" + sb.ToString());
}
static UnityEngine.Material Get(string key, UnityEngine.Color col, UnityEngine.Shader toon,
System.Collections.Generic.Dictionary<string, UnityEngine.Material> cache)
{
UnityEngine.Material m;
if (cache.TryGetValue(key, out m) && m != null) return m;
EnsureDir(FlatDir);
string path = FlatDir + "/Farm_" + Safe(key) + "_ToonFlat.mat";
m = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(path);
if (m == null) { m = new UnityEngine.Material(toon); UnityEditor.AssetDatabase.CreateAsset(m, path); }
m.shader = toon;
m.SetTexture("_BaseMap", null);
m.SetTexture("_ShadowBaseMap", null);
m.SetColor("_DiffuseColor", col);
m.SetColor("_ShadowDiffuseColor", new UnityEngine.Color(col.r * ShadowRatio.r, col.g * ShadowRatio.g, col.b * ShadowRatio.b, 0f));
Common(m);
UnityEditor.EditorUtility.SetDirty(m);
cache[key] = m;
return m;
}
/// <summary>데모 실측 공통값(3DPixelArtEnvironment/Materials/Stone.mat 기준).</summary>
static void Common(UnityEngine.Material m)
{
m.SetFloat("_Shades", 7f);
m.SetFloat("_Brightness", 0.25f);
m.SetFloat("_MinimumDarkness", 0.2f);
m.SetFloat("_AmbientStrength", 0.1f);
m.SetFloat("_OUTLINESENABLED", 1f);
m.SetColor("_Outline", new UnityEngine.Color(0f, 0f, 0f, 0f));
m.SetFloat("_CLOUDSENABLED", 1f);
m.SetFloat("_Cloud_Change", 0.005f); m.SetFloat("_Cloud_Cover", 0.5f);
m.SetFloat("_Cloud_Density", 0.01f); m.SetFloat("_Cloud_Strength", 1f);
m.SetVector("_Cloud_Movement", new UnityEngine.Vector4(1, 1, 0, 0));
m.SetVector("_Cloud_Step", new UnityEngine.Vector4(13, 17, 0, 0));
m.SetFloat("_Cull", 2f);
m.SetFloat("_Surface", 0f);
m.SetFloat("_MAIN_LIGHT", 0f);
m.SetFloat("_SHADOWS_SOFT", 1f);
m.SetFloat("_CastShadows", 1f);
m.SetFloat("_ReceiveShadows", 1f);
m.SetFloat("_NormalEdgeStrength", 0.3f);
m.SetFloat("_DepthEdgeStrength", 0.5f);
m.SetFloat("_DepthThreshold", 0.01f);
m.SetFloat("_NormalThreshold", 1f);
m.SetVector("_NormalBias", new UnityEngine.Vector4(1, 1, 1, 0));
m.EnableKeyword("_OUTLINESENABLED");
m.EnableKeyword("_CLOUDSENABLED");
m.EnableKeyword("_SHADOWS_SOFT");
}
static bool SampleMesh(UnityEngine.Mesh mesh, int sub, UnityEngine.Texture2D lut, out UnityEngine.Color col)
{
col = UnityEngine.Color.white;
try
{
int si = sub < mesh.subMeshCount ? sub : 0;
var uv = mesh.uv; var vt = mesh.vertices;
if (uv == null || uv.Length == 0 || vt == null || vt.Length == 0) return false;
var tri = mesh.GetTriangles(si);
if (tri == null || tri.Length < 3) return false;
double r = 0, g = 0, b = 0, wsum = 0;
for (int t = 0; t + 2 < tri.Length; t += 3)
{
int i0 = tri[t], i1 = tri[t + 1], i2 = tri[t + 2];
if (i0 >= uv.Length || i1 >= uv.Length || i2 >= uv.Length) continue;
float w = UnityEngine.Vector3.Cross(vt[i1] - vt[i0], vt[i2] - vt[i0]).magnitude * 0.5f;
if (w <= 0f) w = 1e-6f;
var c0 = Pick(lut, uv[i0]); var c1 = Pick(lut, uv[i1]); var c2 = Pick(lut, uv[i2]);
r += w * (c0.r + c1.r + c2.r) / 3.0; g += w * (c0.g + c1.g + c2.g) / 3.0; b += w * (c0.b + c1.b + c2.b) / 3.0;
wsum += w;
}
if (wsum <= 0) return false;
col = new UnityEngine.Color((float)(r / wsum), (float)(g / wsum), (float)(b / wsum), 0f);
return true;
}
catch { return false; }
}
static UnityEngine.Color Pick(UnityEngine.Texture2D t, UnityEngine.Vector2 uv)
{
int x = UnityEngine.Mathf.Clamp((int)(uv.x * t.width), 0, t.width - 1);
int y = UnityEngine.Mathf.Clamp((int)(uv.y * t.height), 0, t.height - 1);
return t.GetPixel(x, y);
}
static UnityEngine.Color MeanOf(UnityEngine.Texture2D t)
{
var px = t.GetPixels(); double r = 0, g = 0, b = 0;
for (int i = 0; i < px.Length; i++) { r += px[i].r; g += px[i].g; b += px[i].b; }
return new UnityEngine.Color((float)(r / px.Length), (float)(g / px.Length), (float)(b / px.Length), 0f);
}
static UnityEngine.Texture2D LoadPng(string assetPath)
{
try
{
string full = System.IO.Path.Combine(System.Environment.CurrentDirectory, assetPath);
if (!System.IO.File.Exists(full)) return null;
var t = new UnityEngine.Texture2D(2, 2, UnityEngine.TextureFormat.RGBA32, false, false);
if (!UnityEngine.ImageConversion.LoadImage(t, System.IO.File.ReadAllBytes(full))) return null;
t.filterMode = UnityEngine.FilterMode.Point;
return t;
}
catch { return null; }
}
static string Safe(string s)
{
var sb = new System.Text.StringBuilder();
for (int i = 0; i < s.Length; i++) sb.Append(char.IsLetterOrDigit(s[i]) || s[i] == '_' ? s[i] : '_');
return sb.ToString();
}
static void EnsureDir(string p)
{
if (UnityEditor.AssetDatabase.IsValidFolder(p)) return;
string parent = System.IO.Path.GetDirectoryName(p).Replace('\\', '/');
string leaf = System.IO.Path.GetFileName(p);
if (!UnityEditor.AssetDatabase.IsValidFolder(parent)) EnsureDir(parent);
UnityEditor.AssetDatabase.CreateFolder(parent, leaf);
}
}