mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-27 05:14:27 -05:00
Merge pull request #601 from ousttrue/feature/tpose_button
Feature/tpose button
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,5 +48,7 @@ namespace VRM
|
||||
UseSparseAccessorForMorphTarget = UseSparseAccessor,
|
||||
ExportOnlyBlendShapePosition = OnlyBlendshapePosition,
|
||||
};
|
||||
|
||||
public GameObject Root { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,6 @@ namespace VRM
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// エクスポート時に強制的にT-Pose化する
|
||||
/// </summary>
|
||||
[Tooltip("Option")]
|
||||
public bool ForceTPose = false;
|
||||
|
||||
/// <summary>
|
||||
/// エクスポート時にヒエラルキーの正規化を実施する
|
||||
/// </summary>
|
||||
@@ -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();
|
||||
|
||||
@@ -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<VRMBlendShapeProxy>());
|
||||
}
|
||||
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<BlendShapeKey, float>(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<BlendShapeKey, float>(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)
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace VRM
|
||||
/// <summary>
|
||||
/// ブレンドシェイプを蓄えてまとめて適用するクラス
|
||||
/// </summary>
|
||||
class BlendShapeMerger
|
||||
public class BlendShapeMerger
|
||||
{
|
||||
/// <summary>
|
||||
/// Key からBlendShapeClipを得る
|
||||
|
||||
@@ -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<Animator>();
|
||||
if (animator == null)
|
||||
|
||||
Reference in New Issue
Block a user