diff --git a/Assets/MeshUtility/Editor/ExportDialog/Valildators/HumanoidValidator.cs b/Assets/MeshUtility/Editor/ExportDialog/Valildators/HumanoidValidator.cs
index a1100d465..cc89267b5 100644
--- a/Assets/MeshUtility/Editor/ExportDialog/Valildators/HumanoidValidator.cs
+++ b/Assets/MeshUtility/Editor/ExportDialog/Valildators/HumanoidValidator.cs
@@ -25,6 +25,10 @@ namespace MeshUtility.Validators
[LangMsg(Languages.en, "The model needs to face the positive Z-axis")]
FACE_Z_POSITIVE_DIRECTION,
+ [LangMsg(Languages.ja, "T-Pose にしてください")]
+ [LangMsg(Languages.en, "Set T-Pose")]
+ NOT_TPOSE,
+
[LangMsg(Languages.ja, "ExportRootの Animator に Avatar がありません")]
[LangMsg(Languages.en, "No Avatar in ExportRoot's Animator")]
NO_AVATAR_IN_ANIMATOR,
@@ -37,7 +41,7 @@ namespace MeshUtility.Validators
[LangMsg(Languages.en, "Animator.avatar is not humanoid. Please change model's AnimationType to humanoid")]
AVATAR_IS_NOT_HUMANOID,
- [LangMsg(Languages.ja, "humanoid設定に顎が含まれている。FBX importer の rig 設定に戻って設定を解除することをおすすめします")]
+ [LangMsg(Languages.ja, "Jaw(顎)ボーンが含まれています。意図していない場合は設定解除をおすすめします。FBX importer の rig 設定から変更できます")]
[LangMsg(Languages.en, "Jaw bone is included. It may not what you intended. Please check the humanoid avatar setting screen")]
JAW_BONE_IS_INCLUDED,
}
@@ -150,15 +154,23 @@ namespace MeshUtility.Validators
}
}
+ {
+ var lu = animator.GetBoneTransform(HumanBodyBones.LeftUpperArm);
+ var ll = animator.GetBoneTransform(HumanBodyBones.LeftLowerArm);
+ var ru = animator.GetBoneTransform(HumanBodyBones.RightUpperArm);
+ var rl = animator.GetBoneTransform(HumanBodyBones.RightLowerArm);
+ if (Vector3.Dot((ll.position - lu.position).normalized, Vector3.left) < 0.8f
+ || Vector3.Dot((rl.position - ru.position).normalized, Vector3.right) < 0.8f)
+ {
+ yield return Validation.Error(ValidationMessages.NOT_TPOSE.Msg());
+ }
+ }
+
var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
if (jaw != null)
{
yield return Validation.Warning(ValidationMessages.JAW_BONE_IS_INCLUDED.Msg());
}
- else
- {
- yield return Validation.Info("Animator OK");
- }
}
}
}
diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
index 35205b54a..2e0f2e89e 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
@@ -48,5 +48,7 @@ namespace VRM
UseSparseAccessorForMorphTarget = UseSparseAccessor,
ExportOnlyBlendShapePosition = OnlyBlendshapePosition,
};
+
+ public GameObject Root { get; set; }
}
}
diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs
index e5853dbbb..a4c3512a8 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs
@@ -36,12 +36,6 @@ namespace VRM
}
}
- ///
- /// エクスポート時に強制的にT-Pose化する
- ///
- [Tooltip("Option")]
- public bool ForceTPose = false;
-
///
/// エクスポート時にヒエラルキーの正規化を実施する
///
@@ -78,7 +72,6 @@ namespace VRM
[Tooltip("Remove blendShapeClip that preset is Unknown")]
public bool ReduceBlendshapeClip = false;
- CheckBoxProp m_forceTPose;
CheckBoxProp m_poseFreeze;
CheckBoxProp m_useSparseAccessor;
CheckBoxProp m_onlyBlendShapePosition;
@@ -123,11 +116,22 @@ namespace VRM
[LangMsg(Languages.ja, "エクスポートに頂点カラーを含めない")]
[LangMsg(Languages.en, "Vertex color will not be exported")]
REMOVE_VERTEX_COLOR,
+
+ [LangMsg(Languages.ja, "このボタンで自動で T-Pose にできます。手動で T-Pose にしたり、ボタンの後で手直ししてもOKです。")]
+ [LangMsg(Languages.en, "You can automatically set the T pose with this button. You can change it manually to T-pose or after the button.")]
+ ENALBE_TPOSE_BUTTON,
+
+ [LangMsg(Languages.ja, "このボタンで自動で T-Pose にできます。prefab には実行できません。")]
+ [LangMsg(Languages.en, "You can set the T pose automatically with this button. It cannot be run on prefabs.")]
+ DISABLE_TPOSE_BUTTON,
+
+ [LangMsg(Languages.ja, "T-Pose にする")]
+ [LangMsg(Languages.en, "Make T-Pose")]
+ DO_TPOSE,
}
private void OnEnable()
{
- m_forceTPose = new CheckBoxProp(serializedObject.FindProperty(nameof(ForceTPose)), Options.FORCE_T_POSE);
m_poseFreeze = new CheckBoxProp(serializedObject.FindProperty(nameof(PoseFreeze)), Options.NORMALIZE);
m_useSparseAccessor = new CheckBoxProp(serializedObject.FindProperty(nameof(UseSparseAccessor)), Options.BLENDSHAPE_USE_SPARSE);
m_onlyBlendShapePosition = new CheckBoxProp(serializedObject.FindProperty(nameof(OnlyBlendshapePosition)), Options.BLENDSHAPE_EXCLUDE_NORMAL_AND_TANGENT);
@@ -137,9 +141,33 @@ namespace VRM
public override void OnInspectorGUI()
{
+ GUILayout.Space(20);
+ var settings = (VRMExportSettings)target;
+ var root = settings.Root;
+ var backup = GUI.enabled;
+ GUI.enabled = root.scene.IsValid();
+ if (GUI.enabled)
+ {
+ EditorGUILayout.HelpBox(Options.ENALBE_TPOSE_BUTTON.Msg(), MessageType.Info);
+ }
+ else
+ {
+ EditorGUILayout.HelpBox(Options.DISABLE_TPOSE_BUTTON.Msg(), MessageType.Warning);
+ }
+ if (GUILayout.Button(Options.DO_TPOSE.Msg()))
+ {
+ if (settings.Root)
+ {
+ VRMBoneNormalizer.EnforceTPose(settings.Root);
+ }
+ }
+ GUI.enabled = backup;
+ GUILayout.Space(20);
+
+ // ToDo: 任意の BlendShapeClip を適用する
+
EditorGUIUtility.labelWidth = 160;
serializedObject.Update();
- m_forceTPose.Draw();
m_poseFreeze.Draw();
m_useSparseAccessor.Draw();
m_onlyBlendShapePosition.Draw();
diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
index 1eefffcbf..c13794e1f 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
@@ -23,6 +23,7 @@ namespace VRM
{
Meta,
Mesh,
+ BlendShape,
ExportSettings,
}
Tabs _tab;
@@ -301,18 +302,71 @@ namespace VRM
m_metaEditor.OnInspectorGUI();
break;
- case Tabs.ExportSettings:
- m_settingsInspector.OnInspectorGUI();
- break;
-
case Tabs.Mesh:
m_meshesInspector.OnInspectorGUI();
break;
+
+ case Tabs.BlendShape:
+ if (m_state.ExportRoot)
+ {
+ OnBlendShapeGUI(m_state.ExportRoot.GetComponent());
+ }
+ break;
+
+ case Tabs.ExportSettings:
+ m_settings.Root = m_state.ExportRoot;
+ m_settingsInspector.OnInspectorGUI();
+ break;
}
return true;
}
+ BlendShapeMerger m_merger;
+
+ int m_selected = 0;
+ void OnBlendShapeGUI(VRMBlendShapeProxy proxy)
+ {
+ if (!m_state.ExportRoot.scene.IsValid())
+ {
+ EditorGUILayout.HelpBox("prefab は操作できません", MessageType.Warning);
+ return;
+ }
+
+ if (!proxy)
+ {
+ return;
+ }
+ var avatar = proxy.BlendShapeAvatar;
+ if (!avatar)
+ {
+ return;
+ }
+
+ m_merger = new BlendShapeMerger(avatar.Clips, proxy.transform);
+
+
+ GUILayout.Space(20);
+
+ EditorGUILayout.HelpBox("シーン上のExportRootにBlendShapeを適用します。Exportすると適用された状態がBakeされます。", MessageType.Info);
+
+ var options = avatar.Clips.Select(x => x.ToString()).ToArray();
+ m_selected = EditorGUILayout.Popup("select blendshape", m_selected, options);
+
+ if (GUILayout.Button("選択されたBlendShapeを適用する"))
+ {
+ m_merger.SetValues(avatar.Clips.Select((x, i) => new KeyValuePair(x.Key, i == m_selected ? 1 : 0)));
+ m_merger.Apply();
+ m_settings.PoseFreeze = true;
+ }
+
+ if (GUILayout.Button("BlendShapeをClearする"))
+ {
+ m_merger.SetValues(avatar.Clips.Select(x => new KeyValuePair(x.Key, 0)));
+ m_merger.Apply();
+ }
+ }
+
const string EXTENSION = ".vrm";
private static string m_lastExportDir;
static void OnExportClicked(GameObject root, VRMMetaObject meta, VRMExportSettings settings, VRMExportMeshes meshes)
diff --git a/Assets/VRM/UniVRM/Scripts/BlendShape/BlendShapeMerger.cs b/Assets/VRM/UniVRM/Scripts/BlendShape/BlendShapeMerger.cs
index 02e60ab7b..6fb388ad8 100644
--- a/Assets/VRM/UniVRM/Scripts/BlendShape/BlendShapeMerger.cs
+++ b/Assets/VRM/UniVRM/Scripts/BlendShape/BlendShapeMerger.cs
@@ -11,7 +11,7 @@ namespace VRM
///
/// ブレンドシェイプを蓄えてまとめて適用するクラス
///
- class BlendShapeMerger
+ public class BlendShapeMerger
{
///
/// Key からBlendShapeClipを得る
diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/VRMBoneNormalizer.cs b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/VRMBoneNormalizer.cs
index ce0b8170a..7c5bacee3 100644
--- a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/VRMBoneNormalizer.cs
+++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/VRMBoneNormalizer.cs
@@ -9,7 +9,7 @@ namespace VRM
{
public static class VRMBoneNormalizer
{
- static void EnforceTPose(GameObject go)
+ public static void EnforceTPose(GameObject go)
{
var animator = go.GetComponent();
if (animator == null)