UniVRM/Assets/VRM_Samples/BlendShapeMenu/BlendShapeMenu.cs
2025-02-05 13:56:13 +09:00

182 lines
5.4 KiB
C#

using System.IO;
using System.Text;
using UniGLTF;
using UnityEditor;
using UnityEngine;
namespace VRM.Sample.BlendShapeMenu
{
public static class BlendShapeMenu
{
static string[] NAMES = new string[]
{
"BrowInnerUp",
"BrowDownLeft",
"BrowDownRight",
"BrowOuterUpLeft",
"BrowOuterUpRight",
"EyeLookUpLeft",
"EyeLookUpRight",
"EyeLookDownLeft",
"EyeLookDownRight",
"EyeLookInLeft",
"EyeLookInRight",
"EyeLookOutLeft",
"EyeLookOutRight",
"EyeBlinkLeft",
"EyeBlinkRight",
"EyeSquintRight",
"EyeSquintLeft",
"EyeWideLeft",
"EyeWideRight",
"CheekPuff",
"CheekSquintLeft",
"CheekSquintRight",
"NoseSneerLeft",
"NoseSneerRight",
"JawOpen",
"JawForward",
"JawLeft",
"JawRight",
"MouthFunnel",
"MouthPucker",
"MouthLeft",
"MouthRight",
"MouthRollUpper",
"MouthRollLower",
"MouthShrugUpper",
"MouthShrugLower",
"MouthClose",
"MouthSmileLeft",
"MouthSmileRight",
"MouthFrownLeft",
"MouthFrownRight",
"MouthDimpleLeft",
"MouthDimpleRight",
"MouthUpperUpLeft",
"MouthUpperUpRight",
"MouthLowerDownLeft",
"MouthLowerDownRight",
"MouthPressLeft",
"MouthPressRight",
"MouthStretchLeft",
"MouthStretchRight",
"TongueOut",
};
/// <summary>
/// fullpath C:/path/to/project/Assets/some.preset to Assets/some.preset
/// </summary>
/// <param name="src"></param>
static string ToUnityPath(string src)
{
if (!src.StartsWith(Application.dataPath))
{
// not in Assets
throw new System.Exception("not in Assets");
}
return "Assets" + src.Substring(Application.dataPath.Length);
}
#if UNITY_EDITOR
[MenuItem("CONTEXT/BlendShapeAvatar/Add ARKit FaceTracking BlendShapes")]
public static void AddARKitFaceTrackingBlendShapes(MenuCommand command)
{
var avatar = command.context as BlendShapeAvatar;
if (avatar == null)
{
UniGLTFLogger.Error("no context");
return;
}
var assetPath = AssetDatabase.GetAssetPath(avatar);
var dir = EditorUtility.SaveFolderPanel("blendshape folder", assetPath, "");
if (string.IsNullOrEmpty(dir))
{
// cancel
return;
}
var assetDir = ToUnityPath(dir);
var sb = new StringBuilder();
foreach (var name in NAMES)
{
if (avatar.Clips.Find(x => x != null && x.Preset == BlendShapePreset.Unknown && x.BlendShapeName == name))
{
// already exists
continue;
}
// new blendshape clip
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.Preset = BlendShapePreset.Unknown;
clip.name = name;
clip.BlendShapeName = name;
// write blendshape clip
var clipPath = $"{assetDir}/{name}.asset";
sb.AppendLine($"{clipPath}");
AssetDatabase.CreateAsset(clip, clipPath);
// add to avatar
avatar.Clips.Add(clip);
}
UniGLTFLogger.Log("Create\n" + sb.ToString());
}
[MenuItem("CONTEXT/BlendShapeAvatar/Assign all BlendShapes in a folder")]
static void AssginAllBlendShapesInAFolder(MenuCommand command)
{
var avatar = command.context as BlendShapeAvatar;
if (avatar == null)
{
UniGLTFLogger.Error("no context");
return;
}
var assetPath = AssetDatabase.GetAssetPath(avatar);
var dir = EditorUtility.SaveFolderPanel("blendshape folder", assetPath, "");
if (string.IsNullOrEmpty(dir))
{
// cancel
return;
}
var assetDir = ToUnityPath(dir);
var sb = new StringBuilder();
foreach (var f in Directory.EnumerateFiles(dir))
{
var extension = Path.GetExtension(f).ToLower();
if (extension != ".asset")
{
// not asset
continue;
}
var clipPath = ToUnityPath(f);
if (clipPath == null)
{
// not BlendShapeClip
continue;
}
var clip = AssetDatabase.LoadAssetAtPath<BlendShapeClip>(clipPath);
if (avatar.Clips.Contains(clip))
{
// already exists
continue;
}
sb.AppendLine(clipPath);
avatar.Clips.Add(clip);
}
UniGLTFLogger.Log($"Assign\n" + sb.ToString());
}
#endif
}
}