204 lines
9.8 KiB
C#
204 lines
9.8 KiB
C#
// 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);
|
|
}
|
|
}
|