270 lines
9.8 KiB
C#
270 lines
9.8 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System;
|
|
|
|
namespace VRCDeveloperTool
|
|
{
|
|
public class ShapeKeyNameChanger : EditorWindow
|
|
{
|
|
private List<string> shapeKeyNames;
|
|
private SkinnedMeshRenderer renderer;
|
|
|
|
private string[] posNames;
|
|
|
|
private bool useDuplication = false;
|
|
|
|
private Vector2 scrollPos = Vector2.zero;
|
|
|
|
enum SelectType
|
|
{
|
|
Input,
|
|
Select
|
|
}
|
|
|
|
private SelectType selectTab = SelectType.Input;
|
|
|
|
public static GUIContent[] tabToggles
|
|
{
|
|
get
|
|
{
|
|
return System.Enum.GetNames(typeof(SelectType)).Select(x => new GUIContent(x)).ToArray();
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class JsonData
|
|
{
|
|
public string[] ShapeKeyNames;
|
|
}
|
|
|
|
private string[] selectableNames;
|
|
|
|
private int[] selectedIndices;
|
|
|
|
private void OnEnable()
|
|
{
|
|
shapeKeyNames = null;
|
|
renderer = null;
|
|
posNames = null;
|
|
|
|
selectableNames = LoadJsonDataFromResourceFolder("ShapeKeyNameChanger/shapekeynames");
|
|
}
|
|
|
|
[MenuItem("VRCDeveloperTool/Mesh/ShapeKeyName Changer")]
|
|
private static void Create()
|
|
{
|
|
GetWindow<ShapeKeyNameChanger>("ShapeKeyName Changer");
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
using (var check = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
renderer = EditorGUILayout.ObjectField(
|
|
"Renderer",
|
|
renderer,
|
|
typeof(SkinnedMeshRenderer),
|
|
true
|
|
) as SkinnedMeshRenderer;
|
|
|
|
if (check.changed)
|
|
{
|
|
if (renderer == null)
|
|
{
|
|
shapeKeyNames = null;
|
|
posNames = null;
|
|
selectedIndices = null;
|
|
}
|
|
else
|
|
{
|
|
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
|
|
posNames = shapeKeyNames.ToArray();
|
|
selectedIndices = new int[shapeKeyNames.Count];
|
|
for (int i = 0; i < selectedIndices.Length; i++)
|
|
{
|
|
selectedIndices[i] = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
selectTab = (SelectType)GUILayout.Toolbar((int)selectTab, tabToggles, "LargeButton", GUI.ToolbarButtonSize.Fixed);
|
|
|
|
if (shapeKeyNames != null)
|
|
{
|
|
using (var pos = new GUILayout.ScrollViewScope(scrollPos))
|
|
{
|
|
scrollPos = pos.scrollPosition;
|
|
|
|
if (selectTab == SelectType.Input)
|
|
{
|
|
for (int i = 0; i < shapeKeyNames.Count; i++)
|
|
{
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
using (var toggle = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
EditorGUILayout.Toggle(shapeKeyNames[i] != posNames[i], GUILayout.Width(30));
|
|
if (toggle.changed && shapeKeyNames[i] != posNames[i])
|
|
{
|
|
posNames[i] = shapeKeyNames[i];
|
|
selectedIndices[i] = -1;
|
|
}
|
|
}
|
|
posNames[i] = EditorGUILayout.TextField(shapeKeyNames[i], posNames[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < shapeKeyNames.Count; i++)
|
|
{
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
using (var toggle = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
EditorGUILayout.Toggle(shapeKeyNames[i] != posNames[i], GUILayout.Width(30));
|
|
if (toggle.changed && shapeKeyNames[i] != posNames[i])
|
|
{
|
|
posNames[i] = shapeKeyNames[i];
|
|
selectedIndices[i] = -1;
|
|
}
|
|
}
|
|
|
|
using (var check = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
selectedIndices[i] = EditorGUILayout.Popup(shapeKeyNames[i], selectedIndices[i], selectableNames);
|
|
|
|
if (check.changed && selectedIndices[i] != -1)
|
|
posNames[i] = selectableNames[selectedIndices[i]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
using (new EditorGUI.DisabledScope(renderer == null))
|
|
{
|
|
useDuplication = EditorGUILayout.Toggle("Duplication ShapeKeys", useDuplication);
|
|
|
|
// 대문자 -> 소문자 변환 버튼
|
|
if (GUILayout.Button("Convert First Letter to Lowercase"))
|
|
{
|
|
ConvertFirstLetterToLowercase();
|
|
}
|
|
|
|
// 소문자 -> 대문자 변환 버튼
|
|
if (GUILayout.Button("Convert First Letter to Uppercase"))
|
|
{
|
|
ConvertFirstLetterToUppercase();
|
|
}
|
|
|
|
if (GUILayout.Button("Change ShapeKeyName"))
|
|
{
|
|
CreateNewShapeKeyNameMesh(renderer, posNames, useDuplication, shapeKeyNames);
|
|
|
|
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
|
|
posNames = shapeKeyNames.ToArray();
|
|
selectedIndices = new int[shapeKeyNames.Count];
|
|
for (int i = 0; i < selectedIndices.Length; i++)
|
|
{
|
|
selectedIndices[i] = -1;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// 첫 글자를 소문자로 변환하는 함수
|
|
private void ConvertFirstLetterToLowercase()
|
|
{
|
|
for (int i = 0; i < posNames.Length; i++)
|
|
{
|
|
if (!char.IsLower(posNames[i][0]))
|
|
{
|
|
posNames[i] = char.ToLower(posNames[i][0]) + posNames[i].Substring(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 첫 글자를 대문자로 변환하는 함수
|
|
private void ConvertFirstLetterToUppercase()
|
|
{
|
|
for (int i = 0; i < posNames.Length; i++)
|
|
{
|
|
if (!char.IsUpper(posNames[i][0]))
|
|
{
|
|
posNames[i] = char.ToUpper(posNames[i][0]) + posNames[i].Substring(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool CreateNewShapeKeyNameMesh(SkinnedMeshRenderer renderer, string[] posShapeKeyNames, bool useDuplication, List<string> preShapeKeyNames)
|
|
{
|
|
var mesh = renderer.sharedMesh;
|
|
if (mesh == null) return false;
|
|
|
|
if (posShapeKeyNames.Length != mesh.blendShapeCount) return false;
|
|
|
|
var mesh_custom = UnityEngine.Object.Instantiate<Mesh>(mesh);
|
|
|
|
mesh_custom.ClearBlendShapes();
|
|
|
|
var frameIndex = 0;
|
|
var shapeKeyName = string.Empty;
|
|
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);
|
|
var weight = mesh.GetBlendShapeFrameWeight(blendShapeIndex, frameIndex);
|
|
shapeKeyName = posNames[blendShapeIndex];
|
|
|
|
if (useDuplication && !preShapeKeyNames[blendShapeIndex].Equals(shapeKeyName))
|
|
{
|
|
mesh_custom.AddBlendShapeFrame(preShapeKeyNames[blendShapeIndex], weight, deltaVertices, deltaNormals, deltaTangents);
|
|
}
|
|
|
|
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 + "_custom.asset";
|
|
AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(path));
|
|
AssetDatabase.SaveAssets();
|
|
|
|
return true;
|
|
}
|
|
|
|
private List<string> GetBlendShapeListFromRenderer(SkinnedMeshRenderer renderer)
|
|
{
|
|
List<string> shapeKeyNames = new List<string>();
|
|
var mesh = renderer.sharedMesh;
|
|
|
|
if (mesh != null)
|
|
for (int i = 0; i < mesh.blendShapeCount; i++)
|
|
shapeKeyNames.Add(mesh.GetBlendShapeName(i));
|
|
|
|
return shapeKeyNames;
|
|
}
|
|
|
|
private string[] LoadJsonDataFromResourceFolder(string path)
|
|
{
|
|
var textAsset = Resources.Load<TextAsset>(path);
|
|
var jsonData = JsonUtility.FromJson<JsonData>(textAsset.ToString());
|
|
return jsonData.ShapeKeyNames;
|
|
}
|
|
}
|
|
}
|