add language support in export dialog

This commit is contained in:
PoChangSu 2020-08-17 15:10:32 +09:00
parent 3bbeb96fbd
commit d45ebcdcb9
4 changed files with 149 additions and 32 deletions

View File

@ -86,25 +86,34 @@ namespace VRM
private void OnEnable()
{
m_forceTPose = new CheckBoxProp(serializedObject.FindProperty(nameof(ForceTPose)),
"エクスポート時に強制的にT-Pose化する。これを使わずに手動でT-Poseを作っても問題ありません");
"エクスポート時に強制的にT-Pose化する。これを使わずに手動でT-Poseを作っても問題ありません \n" +
"Force T-Pose before export. Manually making T-Pose for model without enabling this is ok");
m_poseFreeze = new CheckBoxProp(serializedObject.FindProperty(nameof(PoseFreeze)),
"エクスポート時に正規化(ヒエラルキーから回転と拡大縮小を取り除くためにベイク)する");
"エクスポート時に正規化(ヒエラルキーから回転と拡大縮小を取り除くためにベイク)する \n" +
"Model's normalization (bake to remove roation and scaling from the hierarchy)");
m_useExcperimentalExporter = new CheckBoxProp(serializedObject.FindProperty(nameof(UseExperimentalExporter)),
"エクスポート時に新しいJsonSerializerを使う");
"エクスポート時に新しいJsonSerializerを使う \n" +
"The new version of JsonSerializer for model export");
m_useSparseAccessor = new CheckBoxProp(serializedObject.FindProperty(nameof(UseSparseAccessor)),
"BlendShapeの容量を GLTF の Sparse Accessor 機能で削減する。修正中: UniGLTF以外でロードできません");
"BlendShapeの容量を GLTF の Sparse Accessor 機能で削減する。修正中: UniGLTF以外でロードできません \n" +
"BlendShape size can be reduced by using Sparse Accessor");
m_onlyBlendShapePosition = new CheckBoxProp(serializedObject.FindProperty(nameof(OnlyBlendshapePosition)),
"BlendShapeClipのエクスポートに法線とTangentを含めない。UniVRM-0.53 以前ではロードがエラーになるのに注意してください");
"BlendShapeClipのエクスポートに法線とTangentを含めない。UniVRM-0.53 以前ではロードがエラーになるのに注意してください \n" +
"BlendShape's Normal and Tangent will not be exported. Be aware that errors may occur during import if the model is made by UniVRM-0.53 or earlier versions");
m_reduceBlendShape = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshape)),
"BlendShapeClipから参照されないBlendShapeをエクスポートに含めない");
"BlendShapeClipから参照されないBlendShapeをエクスポートに含めない \n" +
"BlendShapes that are not referenced by BlendShapeClips will not be exported");
m_reduceBlendShapeClip = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshapeClip)),
"BlendShapeClip.Preset == Unknown のBlendShapeClipをエクスポートに含めない");
"BlendShapeClip.Preset == Unknown のBlendShapeClipをエクスポートに含めない \n" +
"BlendShapeClip will not be exported if BlendShapeClip.Preset == Unknown");
m_removeVertexColor = new CheckBoxProp(serializedObject.FindProperty(nameof(RemoveVertexColor)),
"エクスポートに頂点カラーを含めない");
"エクスポートに頂点カラーを含めない \n" +
"Vertex color will not be exported");
}
public override void OnInspectorGUI()
{
EditorGUIUtility.labelWidth = 160;
serializedObject.Update();
m_forceTPose.Draw();
m_poseFreeze.Draw();

View File

@ -152,24 +152,24 @@ namespace VRM
if (DuplicateBoneNameExists())
{
yield return Validation.Warning("There is a bone with the same name in the hierarchy. If exported, these bones will be automatically renamed.");
yield return Validation.Warning(Msg.DUPLICATE_BONE_NAME_EXISTS);
}
if (m_settings.ReduceBlendshape && ExportRoot.GetComponent<VRMBlendShapeProxy>() == null)
{
yield return Validation.Error("ReduceBlendshapeSize needs VRMBlendShapeProxy. You need to convert to VRM once.");
yield return Validation.Error(Msg.NEEDS_VRM_BLENDSHAPE_PROXY);
}
var vertexColor = ExportRoot.GetComponentsInChildren<SkinnedMeshRenderer>().Any(x => x.sharedMesh.colors.Length > 0);
if (vertexColor)
{
yield return Validation.Warning("This model contains vertex color");
yield return Validation.Warning(Msg.VERTEX_COLOR_IS_INCLUDED);
}
var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
if (renderers.All(x => !x.gameObject.activeInHierarchy))
{
yield return Validation.Error("No active mesh");
yield return Validation.Error(Msg.NO_ACTIVE_MESH);
}
var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct();
@ -193,15 +193,13 @@ namespace VRM
continue;
}
yield return Validation.Warning(string.Format("{0}: unknown shader '{1}' is used. this will export as `Standard` fallback",
material.name,
material.shader.name));
yield return Validation.Warning($"Material: {material.name}. Unknown Shader: \"{material.shader.name}\" is used. {Msg.UNKNOWN_SHADER}");
}
foreach (var material in materials)
{
if (IsFileNameLengthTooLong(material.name))
yield return Validation.Error(string.Format("FileName '{0}' is too long. ", material.name));
yield return Validation.Error(Msg.FILENAME_TOO_LONG + material.name);
}
var textureNameList = new List<string>();
@ -226,7 +224,7 @@ namespace VRM
foreach (var textureName in textureNameList)
{
if (IsFileNameLengthTooLong(textureName))
yield return Validation.Error(string.Format("FileName '{0}' is too long. ", textureName));
yield return Validation.Error(Msg.FILENAME_TOO_LONG + textureName);
}
var vrmMeta = ExportRoot.GetComponent<VRMMeta>();
@ -234,7 +232,7 @@ namespace VRM
{
var thumbnailName = vrmMeta.Meta.Thumbnail.name;
if (IsFileNameLengthTooLong(thumbnailName))
yield return Validation.Error(string.Format("FileName '{0}' is too long. ", thumbnailName));
yield return Validation.Error(Msg.FILENAME_TOO_LONG + thumbnailName);
}
var meshFilters = ExportRoot.GetComponentsInChildren<MeshFilter>();
@ -242,7 +240,7 @@ namespace VRM
foreach (var meshName in meshesName)
{
if (IsFileNameLengthTooLong(meshName))
yield return Validation.Error(string.Format("FileName '{0}' is too long. ", meshName));
yield return Validation.Error(Msg.FILENAME_TOO_LONG + meshName);
}
var skinnedmeshRenderers = ExportRoot.GetComponentsInChildren<SkinnedMeshRenderer>();
@ -250,7 +248,7 @@ namespace VRM
foreach (var skinnedmeshName in skinnedmeshesName)
{
if (IsFileNameLengthTooLong(skinnedmeshName))
yield return Validation.Error(string.Format("FileName '{0}' is too long. ", skinnedmeshName));
yield return Validation.Error(Msg.FILENAME_TOO_LONG + skinnedmeshName);
}
}
@ -283,7 +281,10 @@ namespace VRM
{
m_Inspector = Editor.CreateEditor(m_settings);
}
m_lang = EnumUtil.TryParseOrDefault<VRMExporterWizardMessages.Languages>(EditorPrefs.GetString(LANG_KEY, default(VRMExporterWizardMessages.Languages).ToString()));
}
const string LANG_KEY = "VRM_LANG";
void OnDisable()
{
@ -334,7 +335,11 @@ namespace VRM
}
}
VRMExporterWizardMessages.Languages m_lang;
VRMExporterWizardMessages.LangMessages Msg => VRMExporterWizardMessages.M17N[m_lang];
//@TODO: Force repaint if scripts recompile
private void OnGUI()
{
if (m_tmpMeta == null)
@ -345,6 +350,14 @@ namespace VRM
EditorGUIUtility.labelWidth = 150;
// lang
var lang = (VRMExporterWizardMessages.Languages)EditorGUILayout.EnumPopup("lang", m_lang);
if (lang != m_lang)
{
m_lang = lang;
EditorPrefs.SetString(LANG_KEY, m_lang.ToString());
}
EditorGUILayout.LabelField("ExportRoot");
var root = (GameObject)EditorGUILayout.ObjectField(ExportRoot, typeof(GameObject), true);
UpdateRoot(root);
@ -358,24 +371,24 @@ namespace VRM
//
if (root == null)
{
Validation.Error("ExportRootをセットしてください").DrawGUI();
Validation.Error(Msg.ROOT_EXISTS).DrawGUI();
return;
}
if (root.transform.parent != null)
{
Validation.Error("ExportRootに親はオブジェクトは持てません").DrawGUI();
Validation.Error(Msg.NO_PARENT).DrawGUI();
return;
}
if (root.transform.localRotation != Quaternion.identity || root.transform.localScale != Vector3.one)
{
Validation.Error("ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください").DrawGUI();
Validation.Error(Msg.ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED).DrawGUI();
return;
}
if (!root.scene.IsValid())
{
// Prefab でシーンに出していないものを判定したい
// FIXME: もっと適切な判定があればそれに
Validation.Error("シーンに出していない Prefab はエクスポートできません(細かい挙動が違い、想定外の動作をところがあるため)。シーンに展開してからエクスポートしてください").DrawGUI();
Validation.Error(Msg.PREFAB_CANNOT_EXPORT).DrawGUI();
return;
}
if (HasRotationOrScale(ExportRoot))
@ -386,14 +399,14 @@ namespace VRM
}
else
{
Validation.Warning("回転・拡大縮小を持つードが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください").DrawGUI();
Validation.Warning(Msg.ROTATION_OR_SCALEING_INCLUDED_IN_NODE).DrawGUI();
}
}
else
{
if (m_settings.PoseFreeze)
{
Validation.Warning("正規化済みです。Setting の PoseFreeze は不要です").DrawGUI();
Validation.Warning(Msg.IS_POSE_FREEZE_DONE).DrawGUI();
}
else
{
@ -407,7 +420,7 @@ namespace VRM
var animator = root.GetComponent<Animator>();
if (animator == null)
{
Validation.Error("ExportRootに Animator がありません").DrawGUI();
Validation.Error(Msg.NO_ANIMATOR).DrawGUI();
return;
}
@ -416,30 +429,30 @@ namespace VRM
var f = GetForward(l, r);
if (Vector3.Dot(f, Vector3.forward) < 0.8f)
{
Validation.Error("Z+ 向きにしてください").DrawGUI();
Validation.Error(Msg.FACE_Z_POSITIVE_DIRECTION).DrawGUI();
return;
}
var avatar = animator.avatar;
if (avatar == null)
{
Validation.Error("ExportRootの Animator に Avatar がありません").DrawGUI();
Validation.Error(Msg.NO_AVATAR_IN_ANIMATOR).DrawGUI();
return;
}
if (!avatar.isValid)
{
Validation.Error("ExportRootの Animator.Avatar が不正です").DrawGUI();
Validation.Error(Msg.AVATAR_IS_NOT_VALID).DrawGUI();
return;
}
if (!avatar.isHuman)
{
Validation.Error("ExportRootの Animator.Avatar がヒューマイドではありません。FBX importer の Rig で設定してください").DrawGUI();
Validation.Error(Msg.AVATAR_IS_NOT_HUMANOID).DrawGUI();
return;
}
var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
if (jaw != null)
{
Validation.Warning("Jaw bone is included. It may not be what you intended. Please check the humanoid avatar setting screen").DrawGUI();
Validation.Warning(Msg.JAW_BONE_IS_INCLUDED).DrawGUI();
}
else
{

View File

@ -0,0 +1,84 @@
using System.Collections.Generic;
namespace VRM
{
/// <summary>
/// エクスポートダイアログ用の簡易なメッセージカタログ
/// </summary>
public static class VRMExporterWizardMessages
{
public enum Languages
{
ja,
en,
}
public struct LangMessages
{
public string ROOT_EXISTS;
public string NO_PARENT;
public string ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED;
public string PREFAB_CANNOT_EXPORT;
public string ROTATION_OR_SCALEING_INCLUDED_IN_NODE;
public string IS_POSE_FREEZE_DONE;
public string NO_ANIMATOR;
public string FACE_Z_POSITIVE_DIRECTION;
public string NO_AVATAR_IN_ANIMATOR;
public string AVATAR_IS_NOT_VALID;
public string AVATAR_IS_NOT_HUMANOID;
public string JAW_BONE_IS_INCLUDED;
public string DUPLICATE_BONE_NAME_EXISTS;
public string NEEDS_VRM_BLENDSHAPE_PROXY;
public string VERTEX_COLOR_IS_INCLUDED;
public string NO_ACTIVE_MESH;
public string UNKNOWN_SHADER;
public string FILENAME_TOO_LONG;
}
public static readonly Dictionary<Languages, LangMessages> M17N = new Dictionary<Languages, LangMessages>
{
{Languages.ja, new LangMessages{
ROOT_EXISTS ="ExportRootをセットしてください",
NO_PARENT = "ExportRootに親はオブジェクトは持てません",
ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED = "ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください",
PREFAB_CANNOT_EXPORT = "シーンに出していない Prefab はエクスポートできません(細かい挙動が違い、想定外の動作をところがあるため)。シーンに展開してからエクスポートしてください",
ROTATION_OR_SCALEING_INCLUDED_IN_NODE = "回転・拡大縮小を持つードが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください",
IS_POSE_FREEZE_DONE = "正規化済みです。Setting の PoseFreeze は不要です",
NO_ANIMATOR = "ExportRootに Animator がありません",
FACE_Z_POSITIVE_DIRECTION = "Z+ 向きにしてください",
NO_AVATAR_IN_ANIMATOR = "ExportRootの Animator に Avatar がありません",
AVATAR_IS_NOT_VALID = "ExportRootの Animator.Avatar が不正です",
AVATAR_IS_NOT_HUMANOID = "ExportRootの Animator.Avatar がヒューマイドではありません。FBX importer の Rig で設定してください",
JAW_BONE_IS_INCLUDED = "humanoid設定に顎が含まれている。FBX importer の rig 設定に戻って設定を解除することをおすすめします",
DUPLICATE_BONE_NAME_EXISTS = "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームする",
NEEDS_VRM_BLENDSHAPE_PROXY = "VRMBlendShapeProxyが必要です。先にVRMフォーマットに変換してください",
VERTEX_COLOR_IS_INCLUDED = "ヒエラルキーに含まれる mesh に頂点カラーが含まれている",
NO_ACTIVE_MESH = "ヒエラルキーに active なメッシュが含まれていない",
UNKNOWN_SHADER = "Standard, Unlit, MToon 以外のマテリアルは、Standard になります",
FILENAME_TOO_LONG = "名前が長すぎる。リネームしてください: ",
}
},
{Languages.en, new LangMessages{
ROOT_EXISTS = "Please set up a ExportRoot for model export",
NO_PARENT = "ExportRoot must be topmost parent",
ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED = "ExportRoot's rotation and scaling are not allowed to change. Please set up rotation and scaling in child node",
PREFAB_CANNOT_EXPORT = "Prefab Asset cannot be exported. Prefab Asset has different behaviour with Scene GameObject. Please put the prefab into the scene",
ROTATION_OR_SCALEING_INCLUDED_IN_NODE = " Normalization is required. There are nodes (child GameObject) where rotation and scaling are not default. Please enable PoseFreeze",
IS_POSE_FREEZE_DONE = "Normalization has been done. PoseFreeze is not required",
NO_ANIMATOR = "No Animator in ExportRoot",
FACE_Z_POSITIVE_DIRECTION = "The model needs to face the positive Z-axis",
NO_AVATAR_IN_ANIMATOR = "No Avatar in ExportRoot's Animator",
AVATAR_IS_NOT_VALID = "Animator.avatar in ExportRoot is not valid",
AVATAR_IS_NOT_HUMANOID = "Animator.avatar is not humanoid. Please change model's AnimationType to humanoid",
JAW_BONE_IS_INCLUDED = "Jaw bone is included. It may not what you intended. Please check the humanoid avatar setting screen",
DUPLICATE_BONE_NAME_EXISTS = "There are bones with the same name in the hierarchy. They will be automatically renamed after export",
NEEDS_VRM_BLENDSHAPE_PROXY = "VRMBlendShapeProxy is required. Please convert to VRM format first",
VERTEX_COLOR_IS_INCLUDED = "This model contains vertex color",
NO_ACTIVE_MESH = "No active mesh",
UNKNOWN_SHADER = "It will export as `Standard` fallback",
FILENAME_TOO_LONG = "FileName is too long: ",
}
},
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 515984b15b5b5bf4baca4fa2d6344fcf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: