using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Linq; using System.IO; namespace VRCDeveloperTool { public class ShapeKeyDeleter : EditorWindow { private SkinnedMeshRenderer renderer; private List shapeKeyNames; private bool[] selectedShapeKeys; private bool isOpenedBlendShape = true; private Vector2 shapeKeyScrollPos = Vector2.zero; private int lastSelectedIndex = -1; // 마지막 선택된 인덱스 저장 [MenuItem("VRCDeveloperTool/Mesh/ShapeKey Deleter")] private static void Open() { GetWindow("ShapeKey Deleter"); } private void OnEnable() { renderer = null; shapeKeyNames = null; selectedShapeKeys = null; isOpenedBlendShape = true; shapeKeyScrollPos = Vector2.zero; } private void OnGUI() { using (var check = new EditorGUI.ChangeCheckScope()) { renderer = EditorGUILayout.ObjectField( "SkinnedMeshRenderer", renderer, typeof(SkinnedMeshRenderer), true ) as SkinnedMeshRenderer; if (check.changed) { if (renderer != null) { shapeKeyNames = GetBlendShapeListFromRenderer(renderer); selectedShapeKeys = new bool[shapeKeyNames.Count()]; } } } if (shapeKeyNames != null) { isOpenedBlendShape = EditorGUILayout.Foldout(isOpenedBlendShape, "Shape Keys"); if (isOpenedBlendShape) { using (new EditorGUI.IndentLevelScope()) using (var scroll = new EditorGUILayout.ScrollViewScope(shapeKeyScrollPos, GUI.skin.box)) { shapeKeyScrollPos = scroll.scrollPosition; Event e = Event.current; for (int i = 0; i < shapeKeyNames.Count(); i++) { bool originalValue = selectedShapeKeys[i]; bool newValue = EditorGUILayout.ToggleLeft(shapeKeyNames[i], selectedShapeKeys[i]); // Shift 클릭 처리 if (e.shift && lastSelectedIndex != -1 && newValue != originalValue) { int startIndex = Mathf.Min(lastSelectedIndex, i); int endIndex = Mathf.Max(lastSelectedIndex, i); for (int j = startIndex; j <= endIndex; j++) { selectedShapeKeys[j] = true; } } // 마지막 선택된 인덱스 갱신 if (newValue != originalValue) { lastSelectedIndex = i; } selectedShapeKeys[i] = newValue; } } } } using (new EditorGUI.DisabledScope(renderer == null || (selectedShapeKeys != null && selectedShapeKeys.All(x => !x)))) { if (GUILayout.Button("Delete ShapeKeys")) { var selectedBlendShapeIndexs = selectedShapeKeys .Select((isSelect, index) => new { Index = index, Value = isSelect }) .Where(x => x.Value) .Select(x => x.Index) .ToArray(); DeleteShapeKey(renderer, selectedBlendShapeIndexs); shapeKeyNames = GetBlendShapeListFromRenderer(renderer); selectedShapeKeys = new bool[shapeKeyNames.Count()]; } } } private bool DeleteShapeKey(SkinnedMeshRenderer renderer, int[] selectedShapeKeyIndexs) { var mesh = renderer.sharedMesh; if (mesh == null) return false; var mesh_custom = Instantiate(mesh); mesh_custom.ClearBlendShapes(); int frameIndex = 0; string shapeKeyName; float weight; Vector3[] deltaVertices, deltaNormals, deltaTangents; for (int blendShapeIndex = 0; blendShapeIndex < mesh.blendShapeCount; blendShapeIndex++) { deltaVertices = new Vector3[mesh.vertexCount]; deltaNormals = new Vector3[mesh.vertexCount]; deltaTangents = new Vector3[mesh.vertexCount]; mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameIndex, deltaVertices, deltaNormals, deltaTangents); weight = mesh.GetBlendShapeFrameWeight(blendShapeIndex, frameIndex); if (!selectedShapeKeyIndexs.Contains(blendShapeIndex)) { shapeKeyName = mesh.GetBlendShapeName(blendShapeIndex); mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents); } } Undo.RecordObject(renderer, "Renderer " + renderer.name); renderer.sharedMesh = mesh_custom; var path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)) + "/" + mesh.name + "_shapekeydeleted.asset"; AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(path)); AssetDatabase.SaveAssets(); return true; } private List GetBlendShapeListFromRenderer(SkinnedMeshRenderer renderer) { List shapeKeyNames = new List(); var mesh = renderer.sharedMesh; if (mesh != null) for (int i = 0; i < mesh.blendShapeCount; i++) shapeKeyNames.Add(mesh.GetBlendShapeName(i)); return shapeKeyNames; } } }