// run_script entry points for #744 shader application (WL = himminji base · Unity 6000.3 · URP 17) // unity command run_script --file AgentScripts/ToonConvert.cs --entry ToonConvert.Report --args '["Assets/ResWork/Map"]' // unity command run_script --file AgentScripts/ToonConvert.cs --entry ToonConvert.MapToToonPro --args '["Assets/ResWork/Map/Content_Map1", true]' // unity command run_script --file AgentScripts/ToonConvert.cs --entry ToonConvert.CharsToUTS --args '["Assets/ResWork/Suriyun/Characters", true]' // Mapping only copies the base/normal/mask textures and colors; toon parameters keep the target shader defaults (tuning is a follow-up). // Materials whose shader is already the target, or that are transparent/particle/UI/unknown, are skipped and listed in the report. using System.Collections.Generic; using System.Text; using UnityEditor; using UnityEngine; public static class ToonConvert { const string ToonProName = "Toon Shaders Pro/URP/Toon"; const string UtsName = "Toon/Toon"; // ---------- survey ---------- public static object Report(string folder) { var guids = AssetDatabase.FindAssets("t:Material", new[] { folder }); var byShader = new Dictionary(); foreach (var g in guids) { var m = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(g)); var n = (m == null || m.shader == null) ? "(null)" : m.shader.name; byShader[n] = byShader.TryGetValue(n, out var c) ? c + 1 : 1; } var sb = new StringBuilder(); sb.Append(folder).Append(" materials=").Append(guids.Length).Append('\n'); foreach (var kv in byShader) sb.Append(" ").Append(kv.Value).Append(" ").Append(kv.Key).Append('\n'); return sb.ToString(); } // ---------- helpers ---------- static Texture Tex(Material m, string prop) => m.HasProperty(prop) ? m.GetTexture(prop) : null; static Vector2 Scale(Material m, string prop) => m.HasProperty(prop) ? m.GetTextureScale(prop) : Vector2.one; static Vector2 Offset(Material m, string prop) => m.HasProperty(prop) ? m.GetTextureOffset(prop) : Vector2.zero; static Color Col(Material m, string prop, Color fallback) => m.HasProperty(prop) ? m.GetColor(prop) : fallback; static float Flt(Material m, string prop, float fallback) => m.HasProperty(prop) ? m.GetFloat(prop) : fallback; // Read a source material into a neutral description regardless of its shader family. class Src { public Texture baseMap; public Vector2 scale = Vector2.one, offset = Vector2.zero; public Color baseColor = Color.white; public Texture normalMap; public float normalScale = 1f; public float smoothness = 0.5f, metallic = 0f, cutoff = 0.5f; public bool alphaClip; public bool transparent; public string family; } static Src ReadSource(Material m) { var s = new Src(); var n = m.shader.name; if (n.StartsWith("MK/Toon")) { s.family = "MKToon"; s.baseMap = Tex(m, "_AlbedoMap"); s.scale = Scale(m, "_AlbedoMap"); s.offset = Offset(m, "_AlbedoMap"); s.baseColor = Col(m, "_AlbedoColor", Color.white); s.normalMap = Tex(m, "_NormalMap"); s.normalScale = Flt(m, "_NormalMapIntensity", 1f); s.smoothness = Flt(m, "_Smoothness", 0.5f); s.cutoff = Flt(m, "_AlphaCutoff", 0.5f); s.alphaClip = Flt(m, "_AlphaClipping", 0f) > 0.5f; s.transparent = Flt(m, "_Surface", 0f) > 0.5f; } else if (n.StartsWith("Universal Render Pipeline/") || n == ToonProName) { s.family = "URP"; s.baseMap = Tex(m, "_BaseMap"); s.scale = Scale(m, "_BaseMap"); s.offset = Offset(m, "_BaseMap"); s.baseColor = Col(m, "_BaseColor", Color.white); s.normalMap = Tex(m, "_BumpMap"); s.normalScale = Flt(m, "_BumpScale", 1f); s.smoothness = Flt(m, "_Smoothness", 0.5f); s.metallic = Flt(m, "_Metallic", 0f); s.cutoff = Flt(m, "_Cutoff", 0.5f); s.alphaClip = Flt(m, "_AlphaClip", 0f) > 0.5f; s.transparent = Flt(m, "_Surface", 0f) > 0.5f; } else if (n == "Standard" || n.StartsWith("Standard (") || n.StartsWith("Legacy Shaders/")) { s.family = "BuiltIn"; s.baseMap = Tex(m, "_MainTex"); s.scale = Scale(m, "_MainTex"); s.offset = Offset(m, "_MainTex"); s.baseColor = Col(m, "_Color", Color.white); s.normalMap = Tex(m, "_BumpMap"); s.normalScale = Flt(m, "_BumpScale", 1f); s.smoothness = Flt(m, "_Glossiness", 0.5f); s.metallic = Flt(m, "_Metallic", 0f); s.cutoff = Flt(m, "_Cutoff", 0.5f); s.alphaClip = Flt(m, "_Mode", 0f) == 1f; s.transparent = Flt(m, "_Mode", 0f) >= 2f; } else if (n == UtsName) { s.family = "UTS"; s.baseMap = Tex(m, "_MainTex"); s.scale = Scale(m, "_MainTex"); s.offset = Offset(m, "_MainTex"); s.baseColor = Col(m, "_BaseColor", Color.white); s.normalMap = Tex(m, "_NormalMap"); s.normalScale = Flt(m, "_BumpScale", 1f); } else s.family = null; return s; } // ---------- map → Toon Shaders Pro ---------- public static object MapToToonPro(string folder, bool dryRun) { var target = Shader.Find(ToonProName); if (target == null) return "ERROR: shader not found: " + ToonProName; var guids = AssetDatabase.FindAssets("t:Material", new[] { folder }); int converted = 0, skipped = 0; var skippedList = new List(); var log = new StringBuilder(); foreach (var g in guids) { var path = AssetDatabase.GUIDToAssetPath(g); var m = AssetDatabase.LoadAssetAtPath(path); if (m == null || m.shader == null) { skipped++; skippedList.Add(path + " (null)"); continue; } if (m.shader.name == ToonProName) { skipped++; continue; } var s = ReadSource(m); if (s.family == null || s.family == "UTS" || s.transparent) { skipped++; skippedList.Add(path + " (" + m.shader.name + (s.transparent ? " transparent" : "") + ")"); continue; } log.Append(path).Append(" : ").Append(m.shader.name).Append(" -> ToonPro\n"); if (dryRun) { converted++; continue; } Undo.RecordObject(m, "ToonConvert"); var queue = m.renderQueue; m.shader = target; m.SetTexture("_BaseMap", s.baseMap); m.SetTextureScale("_BaseMap", s.scale); m.SetTextureOffset("_BaseMap", s.offset); m.SetColor("_BaseColor", s.baseColor); m.SetTexture("_BumpMap", s.normalMap); m.SetFloat("_BumpScale", s.normalScale); if (s.normalMap != null) m.EnableKeyword("_NORMALMAP"); else m.DisableKeyword("_NORMALMAP"); m.SetFloat("_Smoothness", s.smoothness); m.SetFloat("_Metallic", s.metallic); m.SetFloat("_AlphaClip", s.alphaClip ? 1f : 0f); m.SetFloat("_Cutoff", s.cutoff); if (s.alphaClip) m.EnableKeyword("_ALPHATEST_ON"); else m.DisableKeyword("_ALPHATEST_ON"); m.SetFloat("_Surface", 0f); m.renderQueue = s.alphaClip ? 2450 : -1; EditorUtility.SetDirty(m); converted++; } if (!dryRun) AssetDatabase.SaveAssets(); return (dryRun ? "[DRY RUN] " : "") + "MapToToonPro " + folder + " converted=" + converted + " skipped=" + skipped + "\n" + log + "SKIPPED:\n" + string.Join("\n", skippedList); } // ---------- Toon Shaders Pro tint tuning (dark shadow band fix) ---------- // Default converted materials have _ShadowTint = black and _MiddleTint = 0.5, which with ambientIntensity 0 makes every shaded face black. public static object TuneToonPro(string folder, float shadowLum, float middleLum) { var guids = AssetDatabase.FindAssets("t:Material", new[] { folder }); int tuned = 0; foreach (var g in guids) { var m = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(g)); if (m == null || m.shader == null || m.shader.name != ToonProName) continue; Undo.RecordObject(m, "TuneToonPro"); m.SetColor("_ShadowTint", new Color(shadowLum, shadowLum, shadowLum * 1.05f, 1f)); m.SetColor("_MiddleTint", new Color(middleLum, middleLum, middleLum, 1f)); m.SetColor("_LightTint", Color.white); EditorUtility.SetDirty(m); tuned++; } AssetDatabase.SaveAssets(); return "TuneToonPro " + folder + " tuned=" + tuned + " shadow=" + shadowLum + " middle=" + middleLum; } // ---------- characters → UTS (Toon/Toon) ---------- public static object CharsToUTS(string folder, bool dryRun) { var target = Shader.Find(UtsName); if (target == null) return "ERROR: shader not found: " + UtsName; var guids = AssetDatabase.FindAssets("t:Material", new[] { folder }); int converted = 0, skipped = 0; var skippedList = new List(); var log = new StringBuilder(); foreach (var g in guids) { var path = AssetDatabase.GUIDToAssetPath(g); var m = AssetDatabase.LoadAssetAtPath(path); if (m == null || m.shader == null) { skipped++; skippedList.Add(path + " (null)"); continue; } if (m.shader.name == UtsName) { skipped++; continue; } var s = ReadSource(m); if (s.family == null || s.transparent) { skipped++; skippedList.Add(path + " (" + m.shader.name + (s.transparent ? " transparent" : "") + ")"); continue; } log.Append(path).Append(" : ").Append(m.shader.name).Append(" -> UTS\n"); if (dryRun) { converted++; continue; } Undo.RecordObject(m, "ToonConvert"); m.shader = target; m.SetTexture("_MainTex", s.baseMap); m.SetTextureScale("_MainTex", s.scale); m.SetTextureOffset("_MainTex", s.offset); if (m.HasProperty("_BaseMap")) m.SetTexture("_BaseMap", s.baseMap); m.SetColor("_BaseColor", s.baseColor); if (m.HasProperty("_Color")) m.SetColor("_Color", s.baseColor); // shade maps reuse the base map (UTS convention) with slightly darker tints for two-band toon shading m.SetFloat("_Use_BaseAs1st", 1f); m.SetFloat("_Use_1stAs2nd", 1f); m.SetColor("_1st_ShadeColor", s.baseColor * 0.85f); m.SetColor("_2nd_ShadeColor", s.baseColor * 0.7f); m.SetTexture("_NormalMap", s.normalMap); if (m.HasProperty("_BumpScale")) m.SetFloat("_BumpScale", s.normalScale); m.SetFloat("_Is_NormalMapToBase", s.normalMap != null ? 1f : 0f); if (s.alphaClip) { m.SetFloat("_ClippingMode", 1f); m.SetFloat("_Clipping_Level", 1f - s.cutoff); m.SetFloat("_IsBaseMapAlphaAsClippingMask", 1f); } EditorUtility.SetDirty(m); converted++; } if (!dryRun) AssetDatabase.SaveAssets(); return (dryRun ? "[DRY RUN] " : "") + "CharsToUTS " + folder + " converted=" + converted + " skipped=" + skipped + "\n" + log + "SKIPPED:\n" + string.Join("\n", skippedList); } }