188 lines
11 KiB
C#
188 lines
11 KiB
C#
// 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<string, int>();
|
|
foreach (var g in guids)
|
|
{
|
|
var m = AssetDatabase.LoadAssetAtPath<Material>(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<string>(); var log = new StringBuilder();
|
|
foreach (var g in guids)
|
|
{
|
|
var path = AssetDatabase.GUIDToAssetPath(g);
|
|
var m = AssetDatabase.LoadAssetAtPath<Material>(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<Material>(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<string>(); var log = new StringBuilder();
|
|
foreach (var g in guids)
|
|
{
|
|
var path = AssetDatabase.GUIDToAssetPath(g);
|
|
var m = AssetDatabase.LoadAssetAtPath<Material>(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);
|
|
}
|
|
}
|