-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAutoCreateVisemeBlendShapeClips.cs
More file actions
123 lines (112 loc) · 4.57 KB
/
AutoCreateVisemeBlendShapeClips.cs
File metadata and controls
123 lines (112 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using UnityEngine;
using UnityEditor;
using VRM;
using System.Collections.Generic;
public class AutoCreateVisemeBlendShapeClips : MonoBehaviour
{
private static readonly string[] visemeNames = { "A", "I", "U", "E", "O" };
private static readonly string[] japaneseVisemeNames = { "あ", "い", "う", "え", "お" };
[MenuItem("Tools/VRM/Auto-Create Viseme Clips")]
static void CreateVisemeClips()
{
GameObject avatarRoot = Selection.activeGameObject;
if (avatarRoot == null)
{
Debug.LogError("Select the avatar root in the Hierarchy.");
return;
}
var proxy = avatarRoot.GetComponent<VRMBlendShapeProxy>();
if (proxy == null)
{
Debug.LogError("No VRMBlendShapeProxy found on selected object.");
return;
}
// Find the mesh with blendshapes (usually named "Face" or similar)
SkinnedMeshRenderer smr = avatarRoot.GetComponentInChildren<SkinnedMeshRenderer>();
if (smr == null)
{
Debug.LogError("No SkinnedMeshRenderer found in children.");
return;
}
// Map visemes to blendshape indices
int[] blendShapeIndices = new int[visemeNames.Length];
bool foundEnglish = true;
for (int i = 0; i < visemeNames.Length; i++)
{
int idx = smr.sharedMesh.GetBlendShapeIndex(visemeNames[i]);
blendShapeIndices[i] = idx;
if (idx < 0)
foundEnglish = false;
}
// If any English viseme is missing, try Japanese
if (!foundEnglish)
{
Debug.Log("English viseme names not found. Trying Japanese (あ, い, う, え, お)...");
bool foundJapanese = true;
for (int i = 0; i < japaneseVisemeNames.Length; i++)
{
int idx = smr.sharedMesh.GetBlendShapeIndex(japaneseVisemeNames[i]);
blendShapeIndices[i] = idx;
if (idx < 0)
foundJapanese = false;
}
if (!foundJapanese)
{
Debug.LogError("Could not find all Japanese viseme blendshapes ('あ', 'い', 'う', 'え', 'お'). Please check your mesh.");
return;
}
}
// Create folder for BlendShapeClips
string folderPath = "Assets/" + avatarRoot.name + "/BlendShapes";
if (!AssetDatabase.IsValidFolder(folderPath))
{
string parentFolder = "Assets/" + avatarRoot.name;
if (!AssetDatabase.IsValidFolder(parentFolder))
{
AssetDatabase.CreateFolder("Assets", avatarRoot.name);
}
AssetDatabase.CreateFolder(parentFolder, "BlendShapes");
}
// Create BlendShapeClips
var clips = new BlendShapeClip[visemeNames.Length];
for (int i = 0; i < visemeNames.Length; i++)
{
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.BlendShapeName = visemeNames[i];
clip.Preset = (BlendShapePreset)System.Enum.Parse(typeof(BlendShapePreset), visemeNames[i]);
clip.Values = new BlendShapeBinding[]
{
new BlendShapeBinding
{
RelativePath = AnimationUtility.CalculateTransformPath(smr.transform, avatarRoot.transform),
Index = blendShapeIndices[i],
Weight = 100
}
};
string assetPath = $"{folderPath}/{visemeNames[i]}.asset";
AssetDatabase.CreateAsset(clip, assetPath);
clips[i] = clip;
}
// Assign to proxy
var blendShapeAvatar = proxy.BlendShapeAvatar;
if (blendShapeAvatar == null)
{
// If no BlendShapeAvatar exists, create one
blendShapeAvatar = ScriptableObject.CreateInstance<BlendShapeAvatar>();
string avatarAssetPath = $"Assets/{avatarRoot.name}/{avatarRoot.name}_BlendShapeAvatar.asset";
AssetDatabase.CreateAsset(blendShapeAvatar, avatarAssetPath);
proxy.BlendShapeAvatar = blendShapeAvatar;
}
foreach (var clip in clips)
{
if (!blendShapeAvatar.Clips.Contains(clip))
{
blendShapeAvatar.Clips.Add(clip);
}
}
EditorUtility.SetDirty(proxy);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("Viseme BlendShapeClips created and assigned! If your blendshapes were in Japanese, they've been mapped to English visemes for VRM lip sync.");
}
}