#if UNITY_EDITOR using UnityEditor; using UnityEngine; using System.Collections.Generic; using System.IO; public class NiloMaterialMatcapSetter : EditorWindow { private List materials = new List(); private Vector2 scrollPosObjects; private bool enableBackup = true; private string pEnableName_Front = "_BaseMapStackingLayer"; private string pEnableName_Back = "Enable"; private const string targetShaderName = "lilToon"; [MenuItem("Tools/Nilotoon Matcap Auto Mapper", false, 153)] public static void ShowWindow() { GetWindow("닐로툰 매트캡 자동 인식기"); } void OnGUI() { EditorGUILayout.Space(); EditorGUILayout.LabelField("닐로툰 매트캡 자동 인식기", EditorStyles.boldLabel); EditorGUILayout.HelpBox("릴툰에서 변환된 닐로툰 머티리얼 중 누락된 Matcap 정보를 복원합니다.\n선택에 따라 백업본을 생성합니다.", MessageType.Info); EditorGUILayout.Space(); enableBackup = EditorGUILayout.ToggleLeft("💾 머티리얼 백업 생성 (권장)", enableBackup); EditorGUILayout.Space(); EditorGUILayout.LabelField("💠 머티리얼 드래그 앤 드롭", EditorStyles.boldLabel); Rect dropArea = GUILayoutUtility.GetRect(0, 50, GUILayout.ExpandWidth(true)); GUI.Box(dropArea, "여기에 머티리얼을 드래그 하세요", EditorStyles.helpBox); HandleDragAndDrop(dropArea); EditorGUILayout.Space(); EditorGUILayout.LabelField("📝 추가된 머티리얼 목록", EditorStyles.boldLabel); scrollPosObjects = EditorGUILayout.BeginScrollView(scrollPosObjects, GUILayout.Height(150)); foreach (var mat in materials) { EditorGUILayout.ObjectField(mat, typeof(Material), true); } EditorGUILayout.EndScrollView(); EditorGUILayout.Space(); if (GUILayout.Button("자동 복사 실행", GUILayout.Height(30))) { ProcessMaterials(); materials.Clear(); } } void HandleDragAndDrop(Rect dropArea) { Event evt = Event.current; switch (evt.type) { case EventType.DragUpdated: case EventType.DragPerform: if (!dropArea.Contains(evt.mousePosition)) return; DragAndDrop.visualMode = DragAndDropVisualMode.Copy; if (evt.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); foreach (Object draggedObject in DragAndDrop.objectReferences) { if (draggedObject is Material mat && !materials.Contains(mat)) { materials.Add(mat); } } evt.Use(); } break; } } void ProcessMaterials() { Shader lilToonShader = Shader.Find(targetShaderName); if (lilToonShader == null) { Debug.LogError("lilToon Shader를 찾을 수 없습니다. 먼저 프로젝트에 추가해주세요."); return; } foreach (Material originalMaterial in materials) { if (originalMaterial == null) continue; if (enableBackup) { string originalPath = AssetDatabase.GetAssetPath(originalMaterial); string originalDir = Path.GetDirectoryName(originalPath); string backupDir = Path.Combine(originalDir, "M_Backup"); if (!Directory.Exists(backupDir)) { Directory.CreateDirectory(backupDir); AssetDatabase.Refresh(); } string backupPath = Path.Combine(backupDir, originalMaterial.name + "_Backup.mat").Replace("\\", "/"); AssetDatabase.CopyAsset(originalPath, backupPath); AssetDatabase.ImportAsset(backupPath); Debug.Log($"백업 생성됨: {backupPath}"); } Material clonedMaterial = Object.Instantiate(originalMaterial); clonedMaterial.shader = lilToonShader; List matcapInfoList = new List { GetMatcapInfoByName(clonedMaterial, "_UseMatCap", true), GetMatcapInfoByName(clonedMaterial, "_UseMatCap2nd", false) }; foreach (stMatcapInfo matcapInfo in matcapInfoList) { if (!matcapInfo._UseMatCap) continue; int targetLayerNumber = 0; for (int i = 1; i <= 10; i++) { if (originalMaterial.GetFloat(pEnableName_Front + i + pEnableName_Back) < 0.5f) { targetLayerNumber = i; originalMaterial.SetFloat(pEnableName_Front + i + pEnableName_Back, 1f); break; } } if (targetLayerNumber == 0) { Debug.LogWarning($"{originalMaterial.name}: 사용할 수 있는 빈 레이어가 없습니다."); continue; } string prefix = pEnableName_Front + targetLayerNumber; originalMaterial.SetVector(prefix + "TexUVCenterPivotScalePos", new Vector4(1, 1, 0, 0)); originalMaterial.SetVector(prefix + "TexUVScaleOffset", new Vector4(1, 1, 0, 0)); originalMaterial.SetVector(prefix + "TexUVAnimSpeed", Vector4.zero); originalMaterial.SetVector(prefix + "MaskTexChannel", new Vector4(0, 1, 0, 0)); originalMaterial.SetFloat(prefix + "TexUVRotatedAngle", 0); originalMaterial.SetFloat(prefix + "TexUVRotateSpeed", 0); originalMaterial.SetFloat(prefix + "MaskUVIndex", 0); originalMaterial.SetFloat(prefix + "MaskInvertColor", 0); originalMaterial.SetFloat(prefix + "TexIgnoreAlpha", 0); originalMaterial.SetFloat(prefix + "ColorBlendMode", matcapInfo._MatCapBlendMode == 0 ? 0 : matcapInfo._MatCapBlendMode == 1 ? 2 : matcapInfo._MatCapBlendMode == 2 ? 3 : matcapInfo._MatCapBlendMode == 3 ? 4 : 5); originalMaterial.SetTexture(prefix + "Tex", matcapInfo._MatCapTex); originalMaterial.SetColor(prefix + "TintColor", new Color(matcapInfo._MatCapColor.r, matcapInfo._MatCapColor.g, matcapInfo._MatCapColor.b, 1f)); originalMaterial.SetFloat(prefix + "MasterStrength", matcapInfo._MatCapColor.a); originalMaterial.SetFloat(prefix + "TexUVIndex", 4); originalMaterial.SetTexture(prefix + "MaskTex", matcapInfo._MatCapBlendMask); Debug.Log($"{originalMaterial.name}에 Matcap을 Layer {targetLayerNumber}에 적용함"); } DestroyImmediate(clonedMaterial); } AssetDatabase.SaveAssets(); EditorUtility.DisplayDialog("Matcap 복사 완료", "모든 머티리얼에 Matcap 설정을 적용했습니다.", "확인"); } private struct stMatcapInfo { public bool _UseMatCap; public Texture _MatCapTex; public Color _MatCapColor; public Texture _MatCapBlendMask; public int _MatCapBlendMode; public stMatcapInfo(bool use) { _UseMatCap = use; _MatCapTex = null; _MatCapColor = Color.white; _MatCapBlendMask = null; _MatCapBlendMode = 0; } public stMatcapInfo(bool use, Texture tex, Color color, Texture mask, int mode) { _UseMatCap = use; _MatCapTex = tex; _MatCapColor = color; _MatCapBlendMask = mask; _MatCapBlendMode = mode; } } private stMatcapInfo GetMatcapInfoByName(Material mat, string propertyName, bool isFirst) { string suffix = isFirst ? "" : "2nd"; if (mat.HasProperty(propertyName) && mat.GetInt(propertyName) == 1) { return new stMatcapInfo(true, mat.GetTexture("_MatCap" + suffix + "Tex"), mat.GetColor("_MatCap" + suffix + "Color"), mat.GetTexture("_MatCap" + suffix + "BlendMask"), mat.GetInt("_MatCap" + suffix + "BlendMode")); } return new stMatcapInfo(false); } } #endif