mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-04-25 07:28:51 -05:00
Export dialog のバリデーターを整理
* vrm-1.0 と共用にできるように移動 * 例外のcatch
This commit is contained in:
parent
27f64943d8
commit
3c86952228
|
|
@ -113,20 +113,6 @@ namespace MeshUtility
|
|||
helpString = "select target skinnedMesh and animator";
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int IndexOf(Transform[] list, Transform target)
|
||||
{
|
||||
for (int i = 0; i < list.Length; ++i)
|
||||
{
|
||||
if (list[i] == target)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
SkinnedMeshRenderer _Erase(GameObject go)
|
||||
{
|
||||
if (go == null)
|
||||
|
|
|
|||
8
Assets/MeshUtility/Editor/ExportDialog.meta
Normal file
8
Assets/MeshUtility/Editor/ExportDialog.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d7ee61ea6eb4ff541831cba9ce76371f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
106
Assets/MeshUtility/Editor/ExportDialog/ExportDialogState.cs
Normal file
106
Assets/MeshUtility/Editor/ExportDialog/ExportDialogState.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeshUtility
|
||||
{
|
||||
public delegate IEnumerable<Validation> Validator(GameObject root);
|
||||
|
||||
/// <summary>
|
||||
/// Exportダイアログの共通機能
|
||||
///
|
||||
/// * ExportRoot 管理
|
||||
/// * Invalidate 管理
|
||||
///
|
||||
/// </summary>
|
||||
public class ExporterDialogState : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
ExportRoot = null;
|
||||
ExportRootChanged = null;
|
||||
}
|
||||
|
||||
#region ExportRoot管理
|
||||
GameObject m_root;
|
||||
public event Action<GameObject> ExportRootChanged;
|
||||
void RaiseExportRootChanged()
|
||||
{
|
||||
var tmp = ExportRootChanged;
|
||||
if (tmp == null) return;
|
||||
tmp(m_root);
|
||||
}
|
||||
public GameObject ExportRoot
|
||||
{
|
||||
get { return m_root; }
|
||||
set
|
||||
{
|
||||
if (m_root == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_root = value;
|
||||
m_requireValidation = true;
|
||||
RaiseExportRootChanged();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Validation管理
|
||||
bool m_requireValidation = true;
|
||||
public void Invalidate()
|
||||
{
|
||||
// UpdateRoot(ExportRoot);
|
||||
m_requireValidation = true;
|
||||
}
|
||||
|
||||
List<Validation> m_validations = new List<Validation>();
|
||||
public IReadOnlyList<Validation> Validations => m_validations;
|
||||
|
||||
/// <summary>
|
||||
/// EditorWindow.OnGUI から
|
||||
///
|
||||
/// if (Event.current.type == EventType.Layout)
|
||||
/// {
|
||||
/// m_state.Validate(ValidatorFactory());
|
||||
/// }
|
||||
///
|
||||
/// IEnumerable<MeshUtility.Validator> ValidatorFactory()
|
||||
/// {
|
||||
/// yield break;
|
||||
/// }
|
||||
///
|
||||
/// のように呼び出してね
|
||||
/// </summary>
|
||||
public void Validate(IEnumerable<Validator> validators)
|
||||
{
|
||||
if (m_requireValidation)
|
||||
{
|
||||
m_validations.Clear();
|
||||
m_requireValidation = false;
|
||||
foreach (var validator in validators)
|
||||
{
|
||||
foreach (var validation in validator(ExportRoot))
|
||||
{
|
||||
try
|
||||
{
|
||||
m_validations.Add(validation);
|
||||
if (validation.ErrorLevel == ErrorLevels.Critical)
|
||||
{
|
||||
// 打ち切り
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ERROR
|
||||
m_validations.Add(Validation.Critical(ex.Message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7829d135d35830f4bb9235eb10b3de1f
|
||||
guid: 77f1f7ea6f2c8ef4b8f284d9aa0187a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
using System.Linq;
|
||||
using MeshUtility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
public static class ExporterExtensions
|
||||
{
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 84a3f0f4ae5319a46839f08a76ae0edd
|
||||
guid: f76ebfda1e249cb48890de41cc221889
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
100
Assets/MeshUtility/Editor/ExportDialog/M17N.cs
Normal file
100
Assets/MeshUtility/Editor/ExportDialog/M17N.cs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MeshUtility.M17N
|
||||
{
|
||||
/// <summary>
|
||||
/// 多言語対応
|
||||
/// </summary>
|
||||
public enum Languages
|
||||
{
|
||||
ja,
|
||||
en,
|
||||
}
|
||||
|
||||
[System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
|
||||
public class LangMsgAttribute : System.Attribute
|
||||
{
|
||||
public Languages Language;
|
||||
public string Message;
|
||||
|
||||
public LangMsgAttribute(Languages language, string msg)
|
||||
{
|
||||
Language = language;
|
||||
Message = msg;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MsgCache<T> where T : Enum
|
||||
{
|
||||
static Dictionary<Languages, Dictionary<T, string>> s_cache = new Dictionary<Languages, Dictionary<T, string>>();
|
||||
|
||||
static LangMsgAttribute GetAttribute(T value, Languages language)
|
||||
{
|
||||
var t = typeof(T);
|
||||
var memberInfos = t.GetMember(value.ToString());
|
||||
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == t);
|
||||
var attr = enumValueMemberInfo.GetCustomAttributes(typeof(LangMsgAttribute), false).Select(x => (LangMsgAttribute)x).ToArray();
|
||||
if (attr == null || attr.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var match = attr.FirstOrDefault(x => x.Language == language);
|
||||
if (match != null)
|
||||
{
|
||||
return match;
|
||||
}
|
||||
return attr.First();
|
||||
}
|
||||
|
||||
public static string Get(Languages language, T key)
|
||||
{
|
||||
if (!s_cache.TryGetValue(language, out Dictionary<T, string> map))
|
||||
{
|
||||
map = new Dictionary<T, string>();
|
||||
|
||||
var t = typeof(T);
|
||||
foreach (T value in Enum.GetValues(t))
|
||||
{
|
||||
var match = GetAttribute(value, language);
|
||||
// Attribute。無かったら enum の ToString
|
||||
map.Add(value, match != null ? match.Message : key.ToString());
|
||||
}
|
||||
|
||||
s_cache.Add(language, map);
|
||||
}
|
||||
return map[key];
|
||||
}
|
||||
}
|
||||
public static class Getter
|
||||
{
|
||||
const string LANG_KEY = "VRM_LANG";
|
||||
|
||||
static Languages? m_lang;
|
||||
|
||||
public static Languages Lang
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_lang.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public static string Msg<T>(this T key) where T : Enum
|
||||
{
|
||||
return M17N.MsgCache<T>.Get(Lang, key);
|
||||
}
|
||||
|
||||
public static void OnGuiSelectLang()
|
||||
{
|
||||
var lang = (M17N.Languages)EditorGUILayout.EnumPopup("lang", Lang);
|
||||
if (lang != Lang)
|
||||
{
|
||||
m_lang = lang;
|
||||
EditorPrefs.SetString(LANG_KEY, M17N.Getter.Lang.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 515984b15b5b5bf4baca4fa2d6344fcf
|
||||
guid: 3cde586e47f32424a82a4af6c2ce031e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -3,13 +3,25 @@ using System.Linq;
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
|
||||
static class TabBar
|
||||
public static class TabBar
|
||||
{
|
||||
public static T OnGUI<T>(T t, GUIStyle buttonStyle, GUI.ToolbarButtonSize buttonSize) where T : Enum
|
||||
/// <summary>
|
||||
/// GUI.ToolbarButtonSize.FitToContentsも設定できる
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
/// <param name="buttonStyle"></param>
|
||||
/// <param name="buttonSize"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static T OnGUI<T>(T t, GUIStyle buttonStyle = null, GUI.ToolbarButtonSize buttonSize = GUI.ToolbarButtonSize.Fixed) where T : Enum
|
||||
{
|
||||
if (buttonStyle == null)
|
||||
{
|
||||
buttonStyle = "LargeButton";
|
||||
}
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
|
@ -19,6 +31,7 @@ namespace VRM
|
|||
return (T)(object)value;
|
||||
}
|
||||
}
|
||||
|
||||
static class TabCache<T> where T : Enum
|
||||
{
|
||||
private static GUIContent[] _tabToggles = null;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a93e997ef78acca448e14f07cb06e18a
|
||||
guid: 639b5c2a1cd8f5345b7d8a799712e25f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
8
Assets/MeshUtility/Editor/ExportDialog/Valildators.meta
Normal file
8
Assets/MeshUtility/Editor/ExportDialog/Valildators.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1c450c4a7f888424490abbbc29c10a1a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MeshUtility.M17N;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeshUtility.Validators
|
||||
{
|
||||
public static class HierarchyValidator
|
||||
{
|
||||
public enum ExportValidatorMessages
|
||||
{
|
||||
[LangMsg(Languages.ja, "ExportRootをセットしてください")]
|
||||
[LangMsg(Languages.en, "Please set up a ExportRoot for model export")]
|
||||
ROOT_EXISTS,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに親はオブジェクトは持てません")]
|
||||
[LangMsg(Languages.en, "ExportRoot must be topmost parent")]
|
||||
NO_PARENT,
|
||||
|
||||
[LangMsg(Languages.ja, "ヒエラルキーに active なメッシュが含まれていない")]
|
||||
[LangMsg(Languages.en, "No active mesh")]
|
||||
NO_ACTIVE_MESH,
|
||||
|
||||
[LangMsg(Languages.ja, "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームする")]
|
||||
[LangMsg(Languages.en, "There are bones with the same name in the hierarchy. They will be automatically renamed after export")]
|
||||
DUPLICATE_BONE_NAME_EXISTS,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ボーン名の重複を確認
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
static bool DuplicateNodeNameExists(GameObject ExportRoot)
|
||||
{
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var bones = ExportRoot.transform.GetComponentsInChildren<Transform>();
|
||||
var duplicates = bones
|
||||
.GroupBy(p => p.name)
|
||||
.Where(g => g.Count() > 1)
|
||||
.Select(g => g.Key);
|
||||
|
||||
return (duplicates.Any());
|
||||
}
|
||||
|
||||
public static IEnumerable<Validation> Validate(GameObject ExportRoot)
|
||||
{
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
yield return Validation.Critical(ExportValidatorMessages.ROOT_EXISTS.Msg());
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (ExportRoot.transform.parent != null)
|
||||
{
|
||||
yield return Validation.Critical(ExportValidatorMessages.NO_PARENT.Msg());
|
||||
yield break;
|
||||
}
|
||||
|
||||
var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
|
||||
if (renderers.All(x => !x.EnableForExport()))
|
||||
{
|
||||
yield return Validation.Critical(ExportValidatorMessages.NO_ACTIVE_MESH.Msg());
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (DuplicateNodeNameExists(ExportRoot))
|
||||
{
|
||||
yield return Validation.Warning(ExportValidatorMessages.DUPLICATE_BONE_NAME_EXISTS.Msg());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7c870883375b0c14ca5da057cccc84c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MeshUtility.M17N;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeshUtility.Validators
|
||||
{
|
||||
public static class HumanoidValidator
|
||||
{
|
||||
public enum ValidationMessages
|
||||
{
|
||||
[LangMsg(Languages.ja, "回転・拡大縮小もしくはWeightの無いBlendShapeが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください")]
|
||||
[LangMsg(Languages.en, " Normalization is required. There are nodes (child GameObject) where rotation and scaling or blendshape without bone weight are not default. Please enable PoseFreeze")]
|
||||
ROTATION_OR_SCALEING_INCLUDED_IN_NODE,
|
||||
|
||||
[LangMsg(Languages.ja, "正規化済みです。Setting の PoseFreeze は不要です")]
|
||||
[LangMsg(Languages.en, "Normalization has been done. PoseFreeze is not required")]
|
||||
IS_POSE_FREEZE_DONE,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに Animator がありません")]
|
||||
[LangMsg(Languages.en, "No Animator in ExportRoot")]
|
||||
NO_ANIMATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "Z+ 向きにしてください")]
|
||||
[LangMsg(Languages.en, "The model needs to face the positive Z-axis")]
|
||||
FACE_Z_POSITIVE_DIRECTION,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator に Avatar がありません")]
|
||||
[LangMsg(Languages.en, "No Avatar in ExportRoot's Animator")]
|
||||
NO_AVATAR_IN_ANIMATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator.Avatar が不正です")]
|
||||
[LangMsg(Languages.en, "Animator.avatar in ExportRoot is not valid")]
|
||||
AVATAR_IS_NOT_VALID,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator.Avatar がヒューマノイドではありません。FBX importer の Rig で設定してください")]
|
||||
[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.en, "Jaw bone is included. It may not what you intended. Please check the humanoid avatar setting screen")]
|
||||
JAW_BONE_IS_INCLUDED,
|
||||
}
|
||||
|
||||
public static bool HasRotationOrScale(GameObject root)
|
||||
{
|
||||
foreach (var t in root.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
if (t.localRotation != Quaternion.identity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (t.localScale != Vector3.one)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Vector3 GetForward(Transform l, Transform r)
|
||||
{
|
||||
if (l == null || r == null)
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
var lr = (r.position - l.position).normalized;
|
||||
return Vector3.Cross(lr, Vector3.up);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<MeshExportInfo> MeshInformations;
|
||||
public static bool EnableFreeze;
|
||||
|
||||
public static IEnumerable<Validation> Validate(GameObject ExportRoot)
|
||||
{
|
||||
if (!ExportRoot)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (MeshInformations != null)
|
||||
{
|
||||
if (HasRotationOrScale(ExportRoot) || MeshInformations.Any(x => x.ExportBlendShapeCount > 0 && !x.HasSkinning))
|
||||
{
|
||||
// 正規化必用
|
||||
if (EnableFreeze)
|
||||
{
|
||||
// する
|
||||
yield return Validation.Info("PoseFreeze checked. OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
// しない
|
||||
yield return Validation.Warning(ValidationMessages.ROTATION_OR_SCALEING_INCLUDED_IN_NODE.Msg());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 不要
|
||||
if (EnableFreeze)
|
||||
{
|
||||
// する
|
||||
yield return Validation.Warning(ValidationMessages.IS_POSE_FREEZE_DONE.Msg());
|
||||
}
|
||||
else
|
||||
{
|
||||
// しない
|
||||
yield return Validation.Info("Root OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// animator
|
||||
//
|
||||
var animator = ExportRoot.GetComponent<Animator>();
|
||||
if (animator == null)
|
||||
{
|
||||
yield return Validation.Critical(ValidationMessages.NO_ANIMATOR.Msg());
|
||||
yield break;
|
||||
}
|
||||
|
||||
// avatar
|
||||
var avatar = animator.avatar;
|
||||
if (avatar == null)
|
||||
{
|
||||
yield return Validation.Critical(ValidationMessages.NO_AVATAR_IN_ANIMATOR.Msg());
|
||||
yield break;
|
||||
}
|
||||
if (!avatar.isValid)
|
||||
{
|
||||
yield return Validation.Critical(ValidationMessages.AVATAR_IS_NOT_VALID.Msg());
|
||||
yield break;
|
||||
}
|
||||
if (!avatar.isHuman)
|
||||
{
|
||||
yield return Validation.Critical(ValidationMessages.AVATAR_IS_NOT_HUMANOID.Msg());
|
||||
yield break;
|
||||
}
|
||||
// direction
|
||||
{
|
||||
var l = animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg);
|
||||
var r = animator.GetBoneTransform(HumanBodyBones.RightUpperLeg);
|
||||
var f = GetForward(l, r);
|
||||
if (Vector3.Dot(f, Vector3.forward) < 0.8f)
|
||||
{
|
||||
yield return Validation.Critical(ValidationMessages.FACE_Z_POSITIVE_DIRECTION.Msg());
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9013f2150f3c6d943a6f6b6394f7a1ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MeshUtility.M17N;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace MeshUtility.Validators
|
||||
{
|
||||
public static class MaterialValidator
|
||||
{
|
||||
public enum ValidationMessages
|
||||
{
|
||||
[LangMsg(Languages.ja, "Standard, Unlit, MToon 以外のマテリアルは、Standard になります")]
|
||||
[LangMsg(Languages.en, "It will export as `Standard` fallback")]
|
||||
UNKNOWN_SHADER,
|
||||
}
|
||||
|
||||
public static IEnumerable<Validation> Validate(GameObject ExportRoot)
|
||||
{
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
|
||||
foreach (var r in renderers)
|
||||
{
|
||||
for (int i = 0; i < r.sharedMaterials.Length; ++i)
|
||||
if (r.sharedMaterials[i] == null)
|
||||
{
|
||||
yield return Validation.Error($"Renderer: {r.name}.Materials[{i}] is null. please fix it");
|
||||
}
|
||||
}
|
||||
|
||||
var materials = renderers.SelectMany(x => x.sharedMaterials).Where(x => x != null).Distinct();
|
||||
foreach (var material in materials)
|
||||
{
|
||||
if (material == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (material.shader.name == "Standard")
|
||||
{
|
||||
// standard
|
||||
continue;
|
||||
}
|
||||
|
||||
if (UniGLTF.ShaderPropExporter.PreShaderPropExporter.UseUnlit(material.shader.name))
|
||||
{
|
||||
// unlit
|
||||
continue;
|
||||
}
|
||||
|
||||
if (UniGLTF.ShaderPropExporter.PreShaderPropExporter.VRMExtensionShaders.Contains(material.shader.name))
|
||||
{
|
||||
// VRM supported
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return Validation.Warning($"Material: {material.name}. Unknown Shader: \"{material.shader.name}\" is used. {ValidationMessages.UNKNOWN_SHADER.Msg()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aa8f593f0ade99a4d9cc23947cdded6e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MeshUtility.M17N;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeshUtility.Validators
|
||||
{
|
||||
public class NameValidator
|
||||
{
|
||||
public enum ValidationMessages
|
||||
{
|
||||
[LangMsg(Languages.ja, "名前が長すぎる。リネームしてください: ")]
|
||||
[LangMsg(Languages.en, "FileName is too long: ")]
|
||||
FILENAME_TOO_LONG,
|
||||
}
|
||||
|
||||
public static bool IsFileNameLengthTooLong(string fileName)
|
||||
{
|
||||
return fileName.Length > 64;
|
||||
}
|
||||
|
||||
public static IEnumerable<Validation> Validate(GameObject ExportRoot)
|
||||
{
|
||||
var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
|
||||
var materials = renderers.SelectMany(x => x.sharedMaterials).Where(x => x != null).Distinct();
|
||||
foreach (var material in materials)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(material.name))
|
||||
yield return Validation.Error(ValidationMessages.FILENAME_TOO_LONG.Msg() + material.name);
|
||||
}
|
||||
|
||||
var textureNameList = new List<string>();
|
||||
foreach (var material in materials)
|
||||
{
|
||||
var shader = material.shader;
|
||||
int propertyCount = ShaderUtil.GetPropertyCount(shader);
|
||||
for (int i = 0; i < propertyCount; i++)
|
||||
{
|
||||
if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
|
||||
{
|
||||
if ((material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) != null))
|
||||
{
|
||||
var textureName = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)).name;
|
||||
if (!textureNameList.Contains(textureName))
|
||||
textureNameList.Add(textureName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var textureName in textureNameList)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(textureName))
|
||||
yield return Validation.Error(ValidationMessages.FILENAME_TOO_LONG.Msg() + textureName);
|
||||
}
|
||||
|
||||
var meshFilters = ExportRoot.GetComponentsInChildren<MeshFilter>();
|
||||
var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct();
|
||||
foreach (var meshName in meshesName)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(meshName))
|
||||
yield return Validation.Error(ValidationMessages.FILENAME_TOO_LONG.Msg() + meshName);
|
||||
}
|
||||
|
||||
var skinnedmeshRenderers = ExportRoot.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct();
|
||||
foreach (var skinnedmeshName in skinnedmeshesName)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(skinnedmeshName))
|
||||
yield return Validation.Error(ValidationMessages.FILENAME_TOO_LONG.Msg() + skinnedmeshName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 27720b3b18437104b91e44c3f66166ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"name": "MeshUtility.Editor",
|
||||
"references": [
|
||||
"MeshUtility"
|
||||
"MeshUtility",
|
||||
"ShaderProperty.Runtime"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
|
|
|
|||
125
Assets/MeshUtility/Runtime/MeshExportInfo.cs
Normal file
125
Assets/MeshUtility/Runtime/MeshExportInfo.cs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeshUtility
|
||||
{
|
||||
public struct MeshWithRenderer
|
||||
{
|
||||
public Mesh Mesh;
|
||||
[Obsolete("Use Renderer")]
|
||||
public Renderer Rendererer { get { return Renderer; } set { Renderer = value; } }
|
||||
public Renderer Renderer;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct MeshExportInfo
|
||||
{
|
||||
public Renderer Renderer;
|
||||
public Mesh Mesh;
|
||||
public bool IsRendererActive;
|
||||
public bool Skinned;
|
||||
|
||||
public bool HasNormal => Mesh != null && Mesh.normals != null && Mesh.normals.Length == Mesh.vertexCount;
|
||||
public bool HasUV => Mesh != null && Mesh.uv != null && Mesh.uv.Length == Mesh.vertexCount;
|
||||
|
||||
public bool HasVertexColor => Mesh.colors != null && Mesh.colors.Length == Mesh.vertexCount
|
||||
&& VertexColor == VertexColorState.ExistsAndIsUsed
|
||||
|| VertexColor == VertexColorState.ExistsAndMixed // Export する
|
||||
;
|
||||
|
||||
public bool HasSkinning => Mesh.boneWeights != null && Mesh.boneWeights.Length == Mesh.vertexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Mesh に頂点カラーが含まれているか。
|
||||
/// 含まれている場合にマテリアルは Unlit.VColorMultiply になっているか?
|
||||
/// </summary>
|
||||
public enum VertexColorState
|
||||
{
|
||||
// VColorが存在しない
|
||||
None,
|
||||
// VColorが存在して使用している(UnlitはすべてVColorMultiply)
|
||||
ExistsAndIsUsed,
|
||||
// VColorが存在するが使用していない(UnlitはすべてVColorNone。もしくはUnlitが存在しない)
|
||||
ExistsButNotUsed,
|
||||
// VColorが存在して、Unlit.Multiply と Unlit.NotMultiply が混在している。 Unlit.NotMultiply を MToon か Standardに変更した方がよい
|
||||
ExistsAndMixed,
|
||||
}
|
||||
public VertexColorState VertexColor;
|
||||
|
||||
static bool MaterialUseVertexColor(Material m)
|
||||
{
|
||||
if (m == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m.shader.name != UniGLTF.UniUnlit.Utils.ShaderName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (UniGLTF.UniUnlit.Utils.GetVColBlendMode(m) != UniGLTF.UniUnlit.UniUnlitVertexColorBlendOp.Multiply)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static VertexColorState DetectVertexColor(Mesh mesh, Material[] materials)
|
||||
{
|
||||
if (mesh != null && mesh.colors != null && mesh.colors.Length == mesh.vertexCount)
|
||||
{
|
||||
// mesh が 頂点カラーを保持している
|
||||
VertexColorState? state = default;
|
||||
if (materials != null)
|
||||
{
|
||||
foreach (var m in materials)
|
||||
{
|
||||
var currentState = MaterialUseVertexColor(m)
|
||||
? MeshUtility.MeshExportInfo.VertexColorState.ExistsAndIsUsed
|
||||
: MeshUtility.MeshExportInfo.VertexColorState.ExistsButNotUsed
|
||||
;
|
||||
if (state.HasValue)
|
||||
{
|
||||
if (state.Value != currentState)
|
||||
{
|
||||
state = MeshUtility.MeshExportInfo.VertexColorState.ExistsAndMixed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = currentState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return state.GetValueOrDefault(VertexColorState.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
return VertexColorState.None;
|
||||
}
|
||||
}
|
||||
public int VertexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Position, UV, Normal
|
||||
/// [Color]
|
||||
/// [SkinningWeight]
|
||||
/// </summary>
|
||||
public int ExportVertexSize;
|
||||
|
||||
public int IndexCount;
|
||||
|
||||
// int 決め打ち
|
||||
public int IndicesSize => IndexCount * 4;
|
||||
|
||||
public int ExportBlendShapeVertexSize;
|
||||
|
||||
public int TotalBlendShapeCount;
|
||||
|
||||
public int ExportBlendShapeCount;
|
||||
|
||||
public int ExportByteSize => ExportVertexSize * VertexCount + IndicesSize + ExportBlendShapeCount * ExportBlendShapeVertexSize * VertexCount;
|
||||
|
||||
public string Summary;
|
||||
}
|
||||
}
|
||||
11
Assets/MeshUtility/Runtime/MeshExportInfo.cs.meta
Normal file
11
Assets/MeshUtility/Runtime/MeshExportInfo.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2af6d95e4a0e06944b34d891d798b88b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,3 +1,15 @@
|
|||
{
|
||||
"name": "MeshUtility"
|
||||
}
|
||||
{
|
||||
"name": "MeshUtility",
|
||||
"references": [
|
||||
"UniUnlit",
|
||||
"ShaderProperty.Runtime"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
107
Assets/MeshUtility/Runtime/Validation.cs
Normal file
107
Assets/MeshUtility/Runtime/Validation.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace MeshUtility
|
||||
{
|
||||
public enum ErrorLevels
|
||||
{
|
||||
// Exportできる。お知らせ
|
||||
Info,
|
||||
// Exportできる。不具合の可能性
|
||||
Warning,
|
||||
// Exportするために修正が必用
|
||||
Error,
|
||||
// Exportの前提を満たさない
|
||||
Critical,
|
||||
}
|
||||
|
||||
public struct Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// エクスポート可能か否か。
|
||||
/// true のメッセージは警告
|
||||
/// false のメッセージはエラー
|
||||
/// </summary>
|
||||
public readonly ErrorLevels ErrorLevel;
|
||||
|
||||
public bool CanExport
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (ErrorLevel)
|
||||
{
|
||||
case ErrorLevels.Info:
|
||||
case ErrorLevels.Warning:
|
||||
return true;
|
||||
case ErrorLevels.Error:
|
||||
case ErrorLevels.Critical:
|
||||
return false;
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly String Message;
|
||||
|
||||
Validation(ErrorLevels canExport, string message)
|
||||
{
|
||||
ErrorLevel = canExport;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void DrawGUI()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ErrorLevel)
|
||||
{
|
||||
case ErrorLevels.Info:
|
||||
EditorGUILayout.HelpBox(Message, MessageType.Info);
|
||||
break;
|
||||
case ErrorLevels.Warning:
|
||||
EditorGUILayout.HelpBox(Message, MessageType.Warning);
|
||||
break;
|
||||
case ErrorLevels.Critical:
|
||||
case ErrorLevels.Error:
|
||||
EditorGUILayout.HelpBox(Message, MessageType.Error);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static Validation Critical(string msg)
|
||||
{
|
||||
return new Validation(ErrorLevels.Critical, msg);
|
||||
}
|
||||
|
||||
public static Validation Error(string msg)
|
||||
{
|
||||
return new Validation(ErrorLevels.Error, msg);
|
||||
}
|
||||
|
||||
public static Validation Warning(string msg)
|
||||
{
|
||||
return new Validation(ErrorLevels.Warning, msg);
|
||||
}
|
||||
|
||||
public static Validation Info(string msg)
|
||||
{
|
||||
return new Validation(ErrorLevels.Info, msg);
|
||||
}
|
||||
|
||||
public void AddTo(IList<Validation> dst)
|
||||
{
|
||||
dst.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public struct MeshWithRenderer
|
||||
{
|
||||
public Mesh Mesh;
|
||||
[Obsolete("Use Renderer")]
|
||||
public Renderer Rendererer { get { return Renderer; } set { Renderer = value; } }
|
||||
public Renderer Renderer;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct MeshExportInfo
|
||||
{
|
||||
public Renderer Renderer;
|
||||
public Mesh Mesh;
|
||||
public bool IsRendererActive;
|
||||
public bool Skinned;
|
||||
|
||||
public bool HasNormal => Mesh != null && Mesh.normals != null && Mesh.normals.Length == Mesh.vertexCount;
|
||||
public bool HasUV => Mesh != null && Mesh.uv != null && Mesh.uv.Length == Mesh.vertexCount;
|
||||
|
||||
public bool HasVertexColor => Mesh.colors != null && Mesh.colors.Length == Mesh.vertexCount
|
||||
&& VertexColor == VertexColorState.ExistsAndIsUsed
|
||||
|| VertexColor == VertexColorState.ExistsAndMixed // Export する
|
||||
;
|
||||
|
||||
public bool HasSkinning => Mesh.boneWeights != null && Mesh.boneWeights.Length == Mesh.vertexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Mesh に頂点カラーが含まれているか。
|
||||
/// 含まれている場合にマテリアルは Unlit.VColorMultiply になっているか?
|
||||
/// </summary>
|
||||
public enum VertexColorState
|
||||
{
|
||||
// VColorが存在しない
|
||||
None,
|
||||
// VColorが存在して使用している(UnlitはすべてVColorMultiply)
|
||||
ExistsAndIsUsed,
|
||||
// VColorが存在するが使用していない(UnlitはすべてVColorNone。もしくはUnlitが存在しない)
|
||||
ExistsButNotUsed,
|
||||
// VColorが存在して、Unlit.Multiply と Unlit.NotMultiply が混在している。 Unlit.NotMultiply を MToon か Standardに変更した方がよい
|
||||
ExistsAndMixed,
|
||||
}
|
||||
public VertexColorState VertexColor;
|
||||
|
||||
static bool MaterialUseVertexColor(Material m)
|
||||
{
|
||||
if (m == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m.shader.name != UniGLTF.UniUnlit.Utils.ShaderName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (UniGLTF.UniUnlit.Utils.GetVColBlendMode(m) != UniGLTF.UniUnlit.UniUnlitVertexColorBlendOp.Multiply)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static VertexColorState DetectVertexColor(Mesh mesh, Material[] materials)
|
||||
{
|
||||
if (mesh != null && mesh.colors != null && mesh.colors.Length == mesh.vertexCount)
|
||||
{
|
||||
// mesh が 頂点カラーを保持している
|
||||
VertexColorState? state = default;
|
||||
if (materials != null)
|
||||
{
|
||||
foreach (var m in materials)
|
||||
{
|
||||
var currentState = MaterialUseVertexColor(m)
|
||||
? UniGLTF.MeshExportInfo.VertexColorState.ExistsAndIsUsed
|
||||
: UniGLTF.MeshExportInfo.VertexColorState.ExistsButNotUsed
|
||||
;
|
||||
if (state.HasValue)
|
||||
{
|
||||
if (state.Value != currentState)
|
||||
{
|
||||
state = UniGLTF.MeshExportInfo.VertexColorState.ExistsAndMixed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = currentState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return state.GetValueOrDefault(VertexColorState.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
return VertexColorState.None;
|
||||
}
|
||||
}
|
||||
|
||||
public int VertexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Position, UV, Normal
|
||||
/// [Color]
|
||||
/// [SkinningWeight]
|
||||
/// </summary>
|
||||
public int ExportVertexSize;
|
||||
|
||||
public int IndexCount;
|
||||
|
||||
// int 決め打ち
|
||||
public int IndicesSize => IndexCount * 4;
|
||||
|
||||
public int ExportBlendShapeVertexSize;
|
||||
|
||||
public int TotalBlendShapeCount;
|
||||
|
||||
public int ExportBlendShapeCount;
|
||||
|
||||
public int ExportByteSize => ExportVertexSize * VertexCount + IndicesSize + ExportBlendShapeCount * ExportBlendShapeVertexSize * VertexCount;
|
||||
|
||||
public string Summary;
|
||||
}
|
||||
|
||||
public struct MeshExportSettings
|
||||
{
|
||||
// MorphTarget に Sparse Accessor を使う
|
||||
|
|
@ -163,9 +41,9 @@ namespace UniGLTF
|
|||
|
||||
var colorAccessorIndex = -1;
|
||||
|
||||
var vColorState = MeshExportInfo.DetectVertexColor(mesh, materials);
|
||||
if (vColorState == MeshExportInfo.VertexColorState.ExistsAndIsUsed // VColor使っている
|
||||
|| vColorState == MeshExportInfo.VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする)
|
||||
var vColorState = MeshUtility.MeshExportInfo.DetectVertexColor(mesh, materials);
|
||||
if (vColorState == MeshUtility.MeshExportInfo.VertexColorState.ExistsAndIsUsed // VColor使っている
|
||||
|| vColorState == MeshUtility.MeshExportInfo.VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする)
|
||||
)
|
||||
{
|
||||
// UniUnlit で Multiply 設定になっている
|
||||
|
|
@ -373,7 +251,7 @@ namespace UniGLTF
|
|||
}
|
||||
|
||||
public static IEnumerable<(Mesh, glTFMesh, Dictionary<int, int>)> ExportMeshes(glTF gltf, int bufferIndex,
|
||||
List<MeshWithRenderer> unityMeshes, List<Material> unityMaterials,
|
||||
List<MeshUtility.MeshWithRenderer> unityMeshes, List<Material> unityMaterials,
|
||||
MeshExportSettings settings)
|
||||
{
|
||||
for (int i = 0; i < unityMeshes.Count; ++i)
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ namespace UniGLTF
|
|||
|
||||
#region Meshes
|
||||
var unityMeshes = Nodes
|
||||
.Select(x => new MeshWithRenderer
|
||||
.Select(x => new MeshUtility.MeshWithRenderer
|
||||
{
|
||||
Mesh = x.GetSharedMesh(),
|
||||
Renderer = x.GetComponent<Renderer>(),
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using MeshUtility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public static class VRMBlendShapeProxyValidator
|
||||
{
|
||||
public static IEnumerable<Validation> Validate(this VRMBlendShapeProxy p)
|
||||
public static IEnumerable<Validation> Validate(this VRMBlendShapeProxy p, GameObject _)
|
||||
{
|
||||
if (p == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,179 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
|
||||
namespace VRM.M17N
|
||||
{
|
||||
/// <summary>
|
||||
/// 多言語対応
|
||||
/// </summary>
|
||||
public enum Languages
|
||||
{
|
||||
ja,
|
||||
en,
|
||||
}
|
||||
|
||||
[System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
|
||||
public class LangMsgAttribute : System.Attribute
|
||||
{
|
||||
public Languages Language;
|
||||
public string Message;
|
||||
|
||||
public LangMsgAttribute(Languages language, string msg)
|
||||
{
|
||||
Language = language;
|
||||
Message = msg;
|
||||
}
|
||||
}
|
||||
|
||||
public enum VRMExporterWizardMessages
|
||||
{
|
||||
[LangMsg(Languages.ja, "ExportRootをセットしてください")]
|
||||
[LangMsg(Languages.en, "Please set up a ExportRoot for model export")]
|
||||
ROOT_EXISTS,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに親はオブジェクトは持てません")]
|
||||
[LangMsg(Languages.en, "ExportRoot must be topmost parent")]
|
||||
NO_PARENT,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください")]
|
||||
[LangMsg(Languages.en, "ExportRoot's rotation and scaling are not allowed to change. Please set up rotation and scaling in child node")]
|
||||
ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED,
|
||||
|
||||
[LangMsg(Languages.ja, "シーンに出していない Prefab はエクスポートできません(細かい挙動が違い、想定外の動作をところがあるため)。シーンに展開してからエクスポートしてください")]
|
||||
[LangMsg(Languages.en, "Prefab Asset cannot be exported. Prefab Asset has different behaviour with Scene GameObject. Please put the prefab into the scene")]
|
||||
PREFAB_CANNOT_EXPORT,
|
||||
|
||||
[LangMsg(Languages.ja, "回転・拡大縮小を持つノードが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください")]
|
||||
[LangMsg(Languages.en, " Normalization is required. There are nodes (child GameObject) where rotation and scaling are not default. Please enable PoseFreeze")]
|
||||
ROTATION_OR_SCALEING_INCLUDED_IN_NODE,
|
||||
|
||||
[LangMsg(Languages.ja, "正規化済みです。Setting の PoseFreeze は不要です")]
|
||||
[LangMsg(Languages.en, "Normalization has been done. PoseFreeze is not required")]
|
||||
IS_POSE_FREEZE_DONE,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに Animator がありません")]
|
||||
[LangMsg(Languages.en, "No Animator in ExportRoot")]
|
||||
NO_ANIMATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "Z+ 向きにしてください")]
|
||||
[LangMsg(Languages.en, "The model needs to face the positive Z-axis")]
|
||||
FACE_Z_POSITIVE_DIRECTION,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator に Avatar がありません")]
|
||||
[LangMsg(Languages.en, "No Avatar in ExportRoot's Animator")]
|
||||
NO_AVATAR_IN_ANIMATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator.Avatar が不正です")]
|
||||
[LangMsg(Languages.en, "Animator.avatar in ExportRoot is not valid")]
|
||||
AVATAR_IS_NOT_VALID,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator.Avatar がヒューマノイドではありません。FBX importer の Rig で設定してください")]
|
||||
[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.en, "Jaw bone is included. It may not what you intended. Please check the humanoid avatar setting screen")]
|
||||
JAW_BONE_IS_INCLUDED,
|
||||
|
||||
[LangMsg(Languages.ja, "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームする")]
|
||||
[LangMsg(Languages.en, "There are bones with the same name in the hierarchy. They will be automatically renamed after export")]
|
||||
DUPLICATE_BONE_NAME_EXISTS,
|
||||
|
||||
[LangMsg(Languages.ja, "VRMBlendShapeProxyが必要です。先にVRMフォーマットに変換してください")]
|
||||
[LangMsg(Languages.en, "VRMBlendShapeProxy is required. Please convert to VRM format first")]
|
||||
NEEDS_VRM_BLENDSHAPE_PROXY,
|
||||
|
||||
[LangMsg(Languages.en, "This model contains vertex color")]
|
||||
[LangMsg(Languages.ja, "ヒエラルキーに含まれる mesh に頂点カラーが含まれている")]
|
||||
VERTEX_COLOR_IS_INCLUDED,
|
||||
|
||||
[LangMsg(Languages.ja, "ヒエラルキーに active なメッシュが含まれていない")]
|
||||
[LangMsg(Languages.en, "No active mesh")]
|
||||
NO_ACTIVE_MESH,
|
||||
|
||||
[LangMsg(Languages.ja, "Standard, Unlit, MToon 以外のマテリアルは、Standard になります")]
|
||||
[LangMsg(Languages.en, "It will export as `Standard` fallback")]
|
||||
UNKNOWN_SHADER,
|
||||
|
||||
[LangMsg(Languages.ja, "名前が長すぎる。リネームしてください: ")]
|
||||
[LangMsg(Languages.en, "FileName is too long: ")]
|
||||
FILENAME_TOO_LONG,
|
||||
}
|
||||
|
||||
static class MsgCache<T> where T : Enum
|
||||
{
|
||||
static Dictionary<Languages, Dictionary<T, string>> s_cache = new Dictionary<Languages, Dictionary<T, string>>();
|
||||
|
||||
static LangMsgAttribute GetAttribute(T value, Languages language)
|
||||
{
|
||||
var t = typeof(T);
|
||||
var memberInfos = t.GetMember(value.ToString());
|
||||
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == t);
|
||||
var attr = enumValueMemberInfo.GetCustomAttributes(typeof(LangMsgAttribute), false).Select(x => (LangMsgAttribute)x).ToArray();
|
||||
if (attr == null || attr.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var match = attr.FirstOrDefault(x => x.Language == language);
|
||||
if (match != null)
|
||||
{
|
||||
return match;
|
||||
}
|
||||
return attr.First();
|
||||
}
|
||||
|
||||
public static string Get(Languages language, T key)
|
||||
{
|
||||
if (!s_cache.TryGetValue(language, out Dictionary<T, string> map))
|
||||
{
|
||||
map = new Dictionary<T, string>();
|
||||
|
||||
var t = typeof(T);
|
||||
foreach (T value in Enum.GetValues(t))
|
||||
{
|
||||
var match = GetAttribute(value, language);
|
||||
// Attribute。無かったら enum の ToString
|
||||
map.Add(value, match != null ? match.Message : key.ToString());
|
||||
}
|
||||
|
||||
s_cache.Add(language, map);
|
||||
}
|
||||
return map[key];
|
||||
}
|
||||
}
|
||||
public static class Getter
|
||||
{
|
||||
const string LANG_KEY = "VRM_LANG";
|
||||
|
||||
static Languages? m_lang;
|
||||
|
||||
public static Languages Lang
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_lang.HasValue)
|
||||
{
|
||||
m_lang = EnumUtil.TryParseOrDefault<Languages>(EditorPrefs.GetString(LANG_KEY, default(Languages).ToString()));
|
||||
}
|
||||
return m_lang.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string Msg<T>(T key) where T : Enum
|
||||
{
|
||||
return M17N.MsgCache<T>.Get(Lang, key);
|
||||
}
|
||||
|
||||
public static void OnGuiSelectLang()
|
||||
{
|
||||
var lang = (M17N.Languages)EditorGUILayout.EnumPopup("lang", Lang);
|
||||
if (lang != Lang)
|
||||
{
|
||||
m_lang = lang;
|
||||
EditorPrefs.SetString(LANG_KEY, M17N.Getter.Lang.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using UnityEditor;
|
||||
using MeshUtility;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MeshUtility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
|
|
@ -32,7 +33,7 @@ namespace VRM
|
|||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<Validation> Validate(this VRMFirstPerson self)
|
||||
public static IEnumerable<Validation> Validate(this VRMFirstPerson self, GameObject _)
|
||||
{
|
||||
Hierarchy = self.GetComponentsInChildren<Transform>(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace VRM
|
|||
/// </summary>
|
||||
/// <param name="path">出力先</param>
|
||||
/// <param name="settings">エクスポート設定</param>
|
||||
public static void Export(string path, GameObject exportRoot, VRMMetaObject meta, VRMExportSettings settings, IReadOnlyList<MeshExportInfo> info)
|
||||
public static void Export(string path, GameObject exportRoot, VRMMetaObject meta, VRMExportSettings settings, IReadOnlyList<MeshUtility.MeshExportInfo> info)
|
||||
{
|
||||
List<GameObject> destroy = new List<GameObject>();
|
||||
try
|
||||
|
|
@ -137,7 +137,7 @@ namespace VRM
|
|||
/// <param name="settings"></param>
|
||||
/// <param name="destroy">作業が終わったらDestoryするべき一時オブジェクト</param>
|
||||
static void Export(string path, GameObject exportRoot, VRMMetaObject meta,
|
||||
VRMExportSettings settings, IReadOnlyList<UniGLTF.MeshExportInfo> info,
|
||||
VRMExportSettings settings, IReadOnlyList<MeshUtility.MeshExportInfo> info,
|
||||
List<GameObject> destroy)
|
||||
{
|
||||
var target = exportRoot;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MeshUtility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
|
|
@ -55,14 +56,14 @@ namespace VRM
|
|||
return false;
|
||||
}
|
||||
|
||||
public List<UniGLTF.MeshExportInfo> Meshes = new List<UniGLTF.MeshExportInfo>();
|
||||
public List<MeshUtility.MeshExportInfo> Meshes = new List<MeshUtility.MeshExportInfo>();
|
||||
|
||||
public int ExpectedExportByteSize => Meshes.Where(x => x.IsRendererActive).Sum(x => x.ExportByteSize);
|
||||
|
||||
List<Validation> m_validations = new List<Validation>();
|
||||
public IEnumerable<Validation> Validations => m_validations;
|
||||
|
||||
public static void CalcMeshSize(ref UniGLTF.MeshExportInfo info,
|
||||
public static void CalcMeshSize(ref MeshUtility.MeshExportInfo info,
|
||||
string relativePath, VRMExportSettings settings, IReadOnlyList<BlendShapeClip> clips)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
|
@ -132,11 +133,11 @@ namespace VRM
|
|||
sb.Append($") x {info.Mesh.vertexCount}");
|
||||
switch (info.VertexColor)
|
||||
{
|
||||
case UniGLTF.MeshExportInfo.VertexColorState.ExistsAndIsUsed:
|
||||
case UniGLTF.MeshExportInfo.VertexColorState.ExistsAndMixed: // エクスポートする
|
||||
case MeshUtility.MeshExportInfo.VertexColorState.ExistsAndIsUsed:
|
||||
case MeshUtility.MeshExportInfo.VertexColorState.ExistsAndMixed: // エクスポートする
|
||||
sb.Insert(0, "[use vcolor]");
|
||||
break;
|
||||
case UniGLTF.MeshExportInfo.VertexColorState.ExistsButNotUsed:
|
||||
case MeshUtility.MeshExportInfo.VertexColorState.ExistsButNotUsed:
|
||||
sb.Insert(0, "[remove vcolor]");
|
||||
break;
|
||||
}
|
||||
|
|
@ -150,7 +151,7 @@ namespace VRM
|
|||
info.Summary = sb.ToString();
|
||||
}
|
||||
|
||||
bool TryGetMeshInfo(GameObject root, Renderer renderer, IReadOnlyList<BlendShapeClip> clips, VRMExportSettings settings, out UniGLTF.MeshExportInfo info)
|
||||
bool TryGetMeshInfo(GameObject root, Renderer renderer, IReadOnlyList<BlendShapeClip> clips, VRMExportSettings settings, out MeshUtility.MeshExportInfo info)
|
||||
{
|
||||
info = default;
|
||||
if (root == null)
|
||||
|
|
@ -186,7 +187,7 @@ namespace VRM
|
|||
return false;
|
||||
}
|
||||
|
||||
info.VertexColor = UniGLTF.MeshExportInfo.DetectVertexColor(info.Mesh, info.Renderer.sharedMaterials);
|
||||
info.VertexColor = MeshUtility.MeshExportInfo.DetectVertexColor(info.Mesh, info.Renderer.sharedMaterials);
|
||||
|
||||
var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, root.transform);
|
||||
CalcMeshSize(ref info, relativePath, settings, clips);
|
||||
|
|
@ -216,7 +217,7 @@ namespace VRM
|
|||
|
||||
foreach (var renderer in ExportRoot.GetComponentsInChildren<Renderer>(true))
|
||||
{
|
||||
if (TryGetMeshInfo(ExportRoot, renderer, clips, settings, out UniGLTF.MeshExportInfo info))
|
||||
if (TryGetMeshInfo(ExportRoot, renderer, clips, settings, out MeshUtility.MeshExportInfo info))
|
||||
{
|
||||
Meshes.Add(info);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRM.M17N;
|
||||
using MeshUtility.M17N;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
|
|
@ -32,7 +32,7 @@ namespace VRM
|
|||
);
|
||||
}
|
||||
|
||||
void DrawElement(int i, UniGLTF.MeshExportInfo info)
|
||||
void DrawElement(int i, MeshUtility.MeshExportInfo info)
|
||||
{
|
||||
var r = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.Height(EditorGUIUtility.singleLineHeight * 3 + 20));
|
||||
var col0 = 32;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRM.M17N;
|
||||
using MeshUtility.M17N;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
|
|
@ -87,7 +87,7 @@ namespace VRM
|
|||
|
||||
static string Msg(Options key)
|
||||
{
|
||||
return M17N.Getter.Msg(key);
|
||||
return MeshUtility.M17N.Getter.Msg(key);
|
||||
}
|
||||
|
||||
enum Options
|
||||
|
|
|
|||
|
|
@ -1,326 +1,41 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using MeshUtility;
|
||||
using MeshUtility.M17N;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public class VRMExporterValidator
|
||||
public static class VRMExporterValidator
|
||||
{
|
||||
// Allows you to enable and disable the wizard create button, so that the user can not click it.
|
||||
public bool IsValid
|
||||
public enum VRMExporterWizardMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
var hasError = m_validations.Any(x => !x.CanExport);
|
||||
return !hasError && !MetaHasError;
|
||||
}
|
||||
[LangMsg(Languages.ja, "VRMBlendShapeProxyが必要です。先にVRMフォーマットに変換してください")]
|
||||
[LangMsg(Languages.en, "VRMBlendShapeProxy is required. Please convert to VRM format first")]
|
||||
NEEDS_VRM_BLENDSHAPE_PROXY,
|
||||
}
|
||||
|
||||
bool MetaHasError = false;
|
||||
public static bool ReduceBlendshape;
|
||||
|
||||
List<Validation> m_validations = new List<Validation>();
|
||||
public IEnumerable<Validation> Validations => m_validations;
|
||||
|
||||
/// <summary>
|
||||
/// ボーン名の重複を確認
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool DuplicateBoneNameExists(GameObject ExportRoot)
|
||||
{
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var bones = ExportRoot.transform.GetComponentsInChildren<Transform>();
|
||||
var duplicates = bones
|
||||
.GroupBy(p => p.name)
|
||||
.Where(g => g.Count() > 1)
|
||||
.Select(g => g.Key);
|
||||
|
||||
return (duplicates.Any());
|
||||
}
|
||||
|
||||
public static bool IsFileNameLengthTooLong(string fileName)
|
||||
{
|
||||
return fileName.Length > 64;
|
||||
}
|
||||
|
||||
public static bool HasRotationOrScale(GameObject root)
|
||||
{
|
||||
foreach (var t in root.GetComponentsInChildren<Transform>())
|
||||
{
|
||||
if (t.localRotation != Quaternion.identity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (t.localScale != Vector3.one)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Vector3 GetForward(Transform l, Transform r)
|
||||
{
|
||||
if (l == null || r == null)
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
var lr = (r.position - l.position).normalized;
|
||||
return Vector3.Cross(lr, Vector3.up);
|
||||
}
|
||||
|
||||
static string Msg(VRMExporterWizardMessages key)
|
||||
{
|
||||
return M17N.Getter.Msg(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ExportDialogを表示する前に確認する。
|
||||
/// </summary>
|
||||
/// <param name="ExportRoot"></param>
|
||||
/// <param name="m_settings"></param>
|
||||
/// <returns></returns>
|
||||
public bool RootAndHumanoidCheck(GameObject ExportRoot, VRMExportSettings m_settings, IReadOnlyList<UniGLTF.MeshExportInfo> info)
|
||||
{
|
||||
//
|
||||
// root
|
||||
//
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.ROOT_EXISTS)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
if (ExportRoot.transform.parent != null)
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.NO_PARENT)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
|
||||
var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
|
||||
if (renderers.All(x => !x.EnableForExport()))
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.NO_ACTIVE_MESH)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasRotationOrScale(ExportRoot) || info.Any(x => x.ExportBlendShapeCount > 0 && !x.HasSkinning))
|
||||
{
|
||||
// 正規化必用
|
||||
if (m_settings.PoseFreeze)
|
||||
{
|
||||
// する
|
||||
EditorGUILayout.HelpBox("PoseFreeze checked. OK", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// しない
|
||||
Validation.Warning(Msg(VRMExporterWizardMessages.ROTATION_OR_SCALEING_INCLUDED_IN_NODE)).DrawGUI();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 不要
|
||||
if (m_settings.PoseFreeze)
|
||||
{
|
||||
// する
|
||||
Validation.Warning(Msg(VRMExporterWizardMessages.IS_POSE_FREEZE_DONE)).DrawGUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
// しない
|
||||
EditorGUILayout.HelpBox("Root OK", MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// animator
|
||||
//
|
||||
var animator = ExportRoot.GetComponent<Animator>();
|
||||
if (animator == null)
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.NO_ANIMATOR)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
|
||||
var avatar = animator.avatar;
|
||||
if (avatar == null)
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.NO_AVATAR_IN_ANIMATOR)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
if (!avatar.isValid)
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.AVATAR_IS_NOT_VALID)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
if (!avatar.isHuman)
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.AVATAR_IS_NOT_HUMANOID)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
{
|
||||
var l = animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg);
|
||||
var r = animator.GetBoneTransform(HumanBodyBones.RightUpperLeg);
|
||||
var f = GetForward(l, r);
|
||||
if (Vector3.Dot(f, Vector3.forward) < 0.8f)
|
||||
{
|
||||
Validation.Error(Msg(VRMExporterWizardMessages.FACE_Z_POSITIVE_DIRECTION)).DrawGUI();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
|
||||
if (jaw != null)
|
||||
{
|
||||
Validation.Warning(Msg(VRMExporterWizardMessages.JAW_BONE_IS_INCLUDED)).DrawGUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Animator OK", MessageType.Info);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// エクスポート可能か検証する。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Validate(GameObject ExportRoot, VRMExportSettings m_settings, VRMMetaObject meta)
|
||||
{
|
||||
m_validations.Clear();
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var proxy = ExportRoot.GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
m_validations.AddRange(_Validate(ExportRoot, m_settings));
|
||||
m_validations.AddRange(VRMSpringBoneValidator.Validate(ExportRoot));
|
||||
var firstPerson = ExportRoot.GetComponent<VRMFirstPerson>();
|
||||
if (firstPerson != null)
|
||||
{
|
||||
m_validations.AddRange(firstPerson.Validate());
|
||||
}
|
||||
if (proxy != null)
|
||||
{
|
||||
m_validations.AddRange(proxy.Validate());
|
||||
}
|
||||
MetaHasError = meta.Validate().Any();
|
||||
}
|
||||
|
||||
IEnumerable<Validation> _Validate(GameObject ExportRoot, VRMExportSettings m_settings)
|
||||
public static IEnumerable<Validation> Validate(GameObject ExportRoot)
|
||||
{
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (DuplicateBoneNameExists(ExportRoot))
|
||||
if (ReduceBlendshape && ExportRoot.GetComponent<VRMBlendShapeProxy>() == null)
|
||||
{
|
||||
yield return Validation.Warning(Msg(VRMExporterWizardMessages.DUPLICATE_BONE_NAME_EXISTS));
|
||||
}
|
||||
|
||||
if (m_settings.ReduceBlendshape && ExportRoot.GetComponent<VRMBlendShapeProxy>() == null)
|
||||
{
|
||||
yield return Validation.Error(Msg(VRMExporterWizardMessages.NEEDS_VRM_BLENDSHAPE_PROXY));
|
||||
}
|
||||
|
||||
var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
|
||||
foreach (var r in renderers)
|
||||
{
|
||||
for (int i = 0; i < r.sharedMaterials.Length; ++i)
|
||||
if (r.sharedMaterials[i] == null)
|
||||
{
|
||||
yield return Validation.Error($"Renderer: {r.name}.Materials[{i}] is null. please fix it");
|
||||
}
|
||||
}
|
||||
|
||||
var materials = renderers.SelectMany(x => x.sharedMaterials).Where(x => x != null).Distinct();
|
||||
foreach (var material in materials)
|
||||
{
|
||||
if (material == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (material.shader.name == "Standard")
|
||||
{
|
||||
// standard
|
||||
continue;
|
||||
}
|
||||
|
||||
if (VRMMaterialExporter.UseUnlit(material.shader.name))
|
||||
{
|
||||
// unlit
|
||||
continue;
|
||||
}
|
||||
|
||||
if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name))
|
||||
{
|
||||
// VRM supported
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return Validation.Warning($"Material: {material.name}. Unknown Shader: \"{material.shader.name}\" is used. {Msg(VRMExporterWizardMessages.UNKNOWN_SHADER)}");
|
||||
}
|
||||
|
||||
foreach (var material in materials)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(material.name))
|
||||
yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + material.name);
|
||||
}
|
||||
|
||||
var textureNameList = new List<string>();
|
||||
foreach (var material in materials)
|
||||
{
|
||||
var shader = material.shader;
|
||||
int propertyCount = ShaderUtil.GetPropertyCount(shader);
|
||||
for (int i = 0; i < propertyCount; i++)
|
||||
{
|
||||
if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
|
||||
{
|
||||
if ((material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) != null))
|
||||
{
|
||||
var textureName = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)).name;
|
||||
if (!textureNameList.Contains(textureName))
|
||||
textureNameList.Add(textureName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var textureName in textureNameList)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(textureName))
|
||||
yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + textureName);
|
||||
yield return Validation.Error(VRMExporterWizardMessages.NEEDS_VRM_BLENDSHAPE_PROXY.Msg());
|
||||
}
|
||||
|
||||
var vrmMeta = ExportRoot.GetComponent<VRMMeta>();
|
||||
if (vrmMeta != null && vrmMeta.Meta != null && vrmMeta.Meta.Thumbnail != null)
|
||||
{
|
||||
var thumbnailName = vrmMeta.Meta.Thumbnail.name;
|
||||
if (IsFileNameLengthTooLong(thumbnailName))
|
||||
yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + thumbnailName);
|
||||
}
|
||||
|
||||
var meshFilters = ExportRoot.GetComponentsInChildren<MeshFilter>();
|
||||
var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct();
|
||||
foreach (var meshName in meshesName)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(meshName))
|
||||
yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + meshName);
|
||||
}
|
||||
|
||||
var skinnedmeshRenderers = ExportRoot.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct();
|
||||
foreach (var skinnedmeshName in skinnedmeshesName)
|
||||
{
|
||||
if (IsFileNameLengthTooLong(skinnedmeshName))
|
||||
yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + skinnedmeshName);
|
||||
if (MeshUtility.Validators.NameValidator.IsFileNameLengthTooLong(thumbnailName))
|
||||
{
|
||||
yield return Validation.Error(MeshUtility.Validators.NameValidator.ValidationMessages.FILENAME_TOO_LONG.Msg() + thumbnailName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
|
|
@ -6,9 +7,6 @@ using UnityEngine;
|
|||
|
||||
namespace VRM
|
||||
{
|
||||
/// <summary>
|
||||
/// エクスポートダイアログ
|
||||
/// </summary>
|
||||
public class VRMExporterWizard : EditorWindow
|
||||
{
|
||||
const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export humanoid";
|
||||
|
|
@ -16,7 +14,9 @@ namespace VRM
|
|||
[MenuItem(CONVERT_HUMANOID_KEY, false, 1)]
|
||||
private static void ExportFromMenu()
|
||||
{
|
||||
VRMExporterWizard.CreateWizard();
|
||||
var window = (VRMExporterWizard)GetWindow(typeof(VRMExporterWizard));
|
||||
window.titleContent = new GUIContent("VRM Exporter");
|
||||
window.Show();
|
||||
}
|
||||
|
||||
enum Tabs
|
||||
|
|
@ -27,16 +27,7 @@ namespace VRM
|
|||
}
|
||||
Tabs _tab;
|
||||
|
||||
GUIStyle TabButtonStyle => "LargeButton";
|
||||
|
||||
// GUI.ToolbarButtonSize.FitToContentsも設定できる
|
||||
GUI.ToolbarButtonSize TabButtonSize => GUI.ToolbarButtonSize.Fixed;
|
||||
const string EXTENSION = ".vrm";
|
||||
|
||||
private static string m_lastExportDir;
|
||||
|
||||
|
||||
GameObject ExportRoot;
|
||||
MeshUtility.ExporterDialogState m_state;
|
||||
|
||||
VRMExportSettings m_settings;
|
||||
VRMExportMeshes m_meshes;
|
||||
|
|
@ -51,7 +42,6 @@ namespace VRM
|
|||
{
|
||||
return;
|
||||
}
|
||||
m_requireValidation = true;
|
||||
if (m_metaEditor != null)
|
||||
{
|
||||
UnityEditor.Editor.DestroyImmediate(m_metaEditor);
|
||||
|
|
@ -61,71 +51,17 @@ namespace VRM
|
|||
}
|
||||
}
|
||||
|
||||
void UpdateRoot(GameObject root)
|
||||
{
|
||||
if (root == ExportRoot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_requireValidation = true;
|
||||
ExportRoot = root;
|
||||
UnityEditor.Editor.DestroyImmediate(m_metaEditor);
|
||||
m_metaEditor = null;
|
||||
|
||||
if (ExportRoot == null)
|
||||
{
|
||||
Meta = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do validation
|
||||
Validate();
|
||||
|
||||
// default setting
|
||||
m_settings.PoseFreeze =
|
||||
VRMExporterValidator.HasRotationOrScale(ExportRoot)
|
||||
|| m_meshes.Meshes.Any(x => x.ExportBlendShapeCount > 0 && !x.HasSkinning)
|
||||
;
|
||||
|
||||
var meta = ExportRoot.GetComponent<VRMMeta>();
|
||||
if (meta != null)
|
||||
{
|
||||
Meta = meta.Meta;
|
||||
}
|
||||
else
|
||||
{
|
||||
Meta = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Validate()
|
||||
{
|
||||
if (!m_requireValidation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_validator.Validate(ExportRoot, m_settings, Meta != null ? Meta : m_tmpMeta);
|
||||
m_requireValidation = false;
|
||||
m_meshes.SetRoot(ExportRoot, m_settings);
|
||||
}
|
||||
|
||||
VRMMetaObject m_tmpMeta;
|
||||
|
||||
Editor m_metaEditor;
|
||||
Editor m_settingsInspector;
|
||||
Editor m_meshesInspector;
|
||||
|
||||
VRMExporterValidator m_validator = new VRMExporterValidator();
|
||||
bool m_requireValidation = true;
|
||||
|
||||
private Vector2 m_ScrollPosition;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// Debug.Log("OnEnable");
|
||||
Undo.willFlushUndoRecord += OnWizardUpdate;
|
||||
Selection.selectionChanged += OnWizardUpdate;
|
||||
Undo.willFlushUndoRecord += Repaint;
|
||||
Selection.selectionChanged += Repaint;
|
||||
|
||||
m_tmpMeta = ScriptableObject.CreateInstance<VRMMetaObject>();
|
||||
|
||||
|
|
@ -134,15 +70,46 @@ namespace VRM
|
|||
|
||||
m_meshes = ScriptableObject.CreateInstance<VRMExportMeshes>();
|
||||
m_meshesInspector = Editor.CreateEditor(m_meshes);
|
||||
|
||||
m_state = new MeshUtility.ExporterDialogState();
|
||||
m_state.ExportRootChanged += (root) =>
|
||||
{
|
||||
// update meta
|
||||
if (root == null)
|
||||
{
|
||||
Meta = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var meta = root.GetComponent<VRMMeta>();
|
||||
if (meta != null)
|
||||
{
|
||||
Meta = meta.Meta;
|
||||
}
|
||||
else
|
||||
{
|
||||
Meta = null;
|
||||
}
|
||||
|
||||
// default setting
|
||||
m_settings.PoseFreeze =
|
||||
MeshUtility.Validators.HumanoidValidator.HasRotationOrScale(root)
|
||||
|| m_meshes.Meshes.Any(x => x.ExportBlendShapeCount > 0 && !x.HasSkinning)
|
||||
;
|
||||
}
|
||||
|
||||
Repaint();
|
||||
};
|
||||
m_state.ExportRoot = Selection.activeObject as GameObject;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
ExportRoot = null;
|
||||
m_state.Dispose();
|
||||
|
||||
// Debug.Log("OnDisable");
|
||||
Selection.selectionChanged -= OnWizardUpdate;
|
||||
Undo.willFlushUndoRecord -= OnWizardUpdate;
|
||||
Selection.selectionChanged -= Repaint;
|
||||
Undo.willFlushUndoRecord -= Repaint;
|
||||
|
||||
// m_metaEditor
|
||||
UnityEditor.Editor.DestroyImmediate(m_metaEditor);
|
||||
|
|
@ -165,20 +132,6 @@ namespace VRM
|
|||
m_meshes = null;
|
||||
}
|
||||
|
||||
private void InvokeWizardUpdate()
|
||||
{
|
||||
const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
|
||||
MethodInfo method = GetType().GetMethod("OnWizardUpdate", kInstanceInvokeFlags);
|
||||
if (method != null)
|
||||
method.Invoke(this, null);
|
||||
}
|
||||
|
||||
private class Styles
|
||||
{
|
||||
public static string errorText = "Wizard Error";
|
||||
public static string box = "Wizard Box";
|
||||
}
|
||||
|
||||
public delegate Vector2 BeginVerticalScrollViewFunc(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options);
|
||||
static BeginVerticalScrollViewFunc s_func;
|
||||
static BeginVerticalScrollViewFunc BeginVerticalScrollView
|
||||
|
|
@ -194,67 +147,68 @@ namespace VRM
|
|||
return s_func;
|
||||
}
|
||||
}
|
||||
private Vector2 m_ScrollPosition;
|
||||
|
||||
IEnumerable<MeshUtility.Validator> ValidatorFactory()
|
||||
{
|
||||
MeshUtility.Validators.HumanoidValidator.MeshInformations = m_meshes.Meshes;
|
||||
MeshUtility.Validators.HumanoidValidator.EnableFreeze = m_settings.PoseFreeze;
|
||||
VRMExporterValidator.ReduceBlendshape = m_settings.ReduceBlendshape;
|
||||
|
||||
yield return MeshUtility.Validators.HierarchyValidator.Validate;
|
||||
if (!m_state.ExportRoot)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return MeshUtility.Validators.HumanoidValidator.Validate;
|
||||
yield return VRMExporterValidator.Validate;
|
||||
yield return VRMSpringBoneValidator.Validate;
|
||||
|
||||
var firstPerson = m_state.ExportRoot.GetComponent<VRMFirstPerson>();
|
||||
if (firstPerson != null)
|
||||
{
|
||||
yield return firstPerson.Validate;
|
||||
}
|
||||
|
||||
var proxy = m_state.ExportRoot.GetComponent<VRMBlendShapeProxy>();
|
||||
if (proxy != null)
|
||||
{
|
||||
yield return proxy.Validate;
|
||||
}
|
||||
|
||||
var meta = Meta ? Meta : m_tmpMeta;
|
||||
yield return meta.Validate;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (m_tmpMeta == null)
|
||||
{
|
||||
// OnDisable
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUIUtility.labelWidth = 150;
|
||||
|
||||
// lang
|
||||
M17N.Getter.OnGuiSelectLang();
|
||||
|
||||
EditorGUILayout.LabelField("ExportRoot");
|
||||
{
|
||||
var root = (GameObject)EditorGUILayout.ObjectField(ExportRoot, typeof(GameObject), true);
|
||||
UpdateRoot(root);
|
||||
}
|
||||
|
||||
// ArgumentException: Getting control 1's position in a group with only 1 controls when doing repaint Aborting
|
||||
// Validation により GUI の表示項目が変わる場合があるので、
|
||||
// EventType.Layout と EventType.Repaint 間で内容が変わらないようしている。
|
||||
if (Event.current.type == EventType.Layout)
|
||||
{
|
||||
Validate();
|
||||
// m_settings, m_meshes.Meshes
|
||||
m_meshes.SetRoot(m_state.ExportRoot, m_settings);
|
||||
m_state.Validate(ValidatorFactory());
|
||||
}
|
||||
|
||||
//
|
||||
// Humanoid として適正か? ここで失敗する場合は Export UI を表示しない
|
||||
//
|
||||
if (!m_validator.RootAndHumanoidCheck(ExportRoot, m_settings, m_meshes.Meshes))
|
||||
EditorGUIUtility.labelWidth = 150;
|
||||
|
||||
// lang
|
||||
MeshUtility.M17N.Getter.OnGuiSelectLang();
|
||||
|
||||
EditorGUILayout.LabelField("ExportRoot");
|
||||
{
|
||||
return;
|
||||
m_state.ExportRoot = (GameObject)EditorGUILayout.ObjectField(m_state.ExportRoot, typeof(GameObject), true);
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox($"Mesh size: {m_meshes.ExpectedExportByteSize / 1000000.0f:0.0} MByte", MessageType.Info);
|
||||
_tab = TabBar.OnGUI(_tab, TabButtonStyle, TabButtonSize);
|
||||
|
||||
// Render contents using Generic Inspector GUI
|
||||
m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box");
|
||||
GUIUtility.GetControlID(645789, FocusType.Passive);
|
||||
|
||||
//
|
||||
// VRM の Validation
|
||||
//
|
||||
foreach (var v in m_validator.Validations)
|
||||
{
|
||||
v.DrawGUI();
|
||||
}
|
||||
foreach (var meshInfo in m_meshes.Meshes)
|
||||
{
|
||||
switch (meshInfo.VertexColor)
|
||||
{
|
||||
case UniGLTF.MeshExportInfo.VertexColorState.ExistsAndMixed:
|
||||
Validation.Warning($"{meshInfo.Renderer}: Both vcolor.multiply and not multiply unlit materials exist").DrawGUI();
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool modified = ScrollArea();
|
||||
|
||||
bool modified = DrawWizardGUI();
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
// Create and Other Buttons
|
||||
|
|
@ -266,11 +220,11 @@ namespace VRM
|
|||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUI.enabled = m_validator.IsValid;
|
||||
GUI.enabled = m_state.Validations.All(x => x.CanExport);
|
||||
|
||||
if (GUILayout.Button("Export", GUILayout.MinWidth(100)))
|
||||
{
|
||||
OnWizardCreate();
|
||||
OnExportClicked(m_state.ExportRoot, Meta != null ? Meta : m_tmpMeta, m_settings, m_meshes);
|
||||
Close();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
|
|
@ -285,11 +239,42 @@ namespace VRM
|
|||
|
||||
if (modified)
|
||||
{
|
||||
m_requireValidation = true;
|
||||
Repaint();
|
||||
m_state.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
bool ScrollArea()
|
||||
{
|
||||
//
|
||||
// Validation
|
||||
//
|
||||
foreach (var v in m_state.Validations)
|
||||
{
|
||||
v.DrawGUI();
|
||||
if (v.ErrorLevel == MeshUtility.ErrorLevels.Critical)
|
||||
{
|
||||
// Export UI を表示しない
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EditorGUILayout.HelpBox($"Mesh size: {m_meshes.ExpectedExportByteSize / 1000000.0f:0.0} MByte", MessageType.Info);
|
||||
|
||||
//
|
||||
// GUI
|
||||
//
|
||||
_tab = MeshUtility.TabBar.OnGUI(_tab);
|
||||
foreach (var meshInfo in m_meshes.Meshes)
|
||||
{
|
||||
switch (meshInfo.VertexColor)
|
||||
{
|
||||
case MeshUtility.MeshExportInfo.VertexColorState.ExistsAndMixed:
|
||||
MeshUtility.Validation.Warning($"{meshInfo.Renderer}: Both vcolor.multiply and not multiply unlit materials exist").DrawGUI();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DrawWizardGUI();
|
||||
}
|
||||
|
||||
bool DrawWizardGUI()
|
||||
{
|
||||
if (m_tmpMeta == null)
|
||||
|
|
@ -328,36 +313,9 @@ namespace VRM
|
|||
return true;
|
||||
}
|
||||
|
||||
// Creates a wizard.
|
||||
public static VRMExporterWizard DisplayWizard()
|
||||
{
|
||||
VRMExporterWizard wizard = CreateInstance<VRMExporterWizard>();
|
||||
wizard.titleContent = new GUIContent("VRM Exporter");
|
||||
if (wizard != null)
|
||||
{
|
||||
wizard.InvokeWizardUpdate();
|
||||
wizard.ShowUtility();
|
||||
}
|
||||
return wizard;
|
||||
}
|
||||
|
||||
public static void CreateWizard()
|
||||
{
|
||||
var wiz = VRMExporterWizard.DisplayWizard();
|
||||
var go = Selection.activeObject as GameObject;
|
||||
|
||||
// update checkbox
|
||||
wiz.UpdateRoot(go);
|
||||
|
||||
if (go != null)
|
||||
{
|
||||
wiz.m_settings.PoseFreeze = VRMExporterValidator.HasRotationOrScale(go);
|
||||
}
|
||||
|
||||
wiz.OnWizardUpdate();
|
||||
}
|
||||
|
||||
void OnWizardCreate()
|
||||
const string EXTENSION = ".vrm";
|
||||
private static string m_lastExportDir;
|
||||
static void OnExportClicked(GameObject root, VRMMetaObject meta, VRMExportSettings settings, VRMExportMeshes meshes)
|
||||
{
|
||||
string directory;
|
||||
if (string.IsNullOrEmpty(m_lastExportDir))
|
||||
|
|
@ -369,7 +327,7 @@ namespace VRM
|
|||
var path = EditorUtility.SaveFilePanel(
|
||||
"Save vrm",
|
||||
directory,
|
||||
ExportRoot.name + EXTENSION,
|
||||
root.name + EXTENSION,
|
||||
EXTENSION.Substring(1));
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
|
|
@ -378,14 +336,7 @@ namespace VRM
|
|||
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
|
||||
|
||||
// export
|
||||
VRMEditorExporter.Export(path, ExportRoot, Meta != null ? Meta : m_tmpMeta, m_settings, m_meshes.Meshes);
|
||||
}
|
||||
|
||||
void OnWizardUpdate()
|
||||
{
|
||||
UpdateRoot(ExportRoot);
|
||||
m_requireValidation = true;
|
||||
Repaint();
|
||||
VRMEditorExporter.Export(path, root, meta, settings, meshes.Meshes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
using VRM.M17N;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public enum VRMExporterWizardMessages
|
||||
{
|
||||
[LangMsg(Languages.ja, "ExportRootをセットしてください")]
|
||||
[LangMsg(Languages.en, "Please set up a ExportRoot for model export")]
|
||||
ROOT_EXISTS,
|
||||
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに親はオブジェクトは持てません")]
|
||||
[LangMsg(Languages.en, "ExportRoot must be topmost parent")]
|
||||
NO_PARENT,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください")]
|
||||
[LangMsg(Languages.en, "ExportRoot's rotation and scaling are not allowed to change. Please set up rotation and scaling in child node")]
|
||||
ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED,
|
||||
|
||||
[LangMsg(Languages.ja, "シーンに出していない Prefab はエクスポートできません(細かい挙動が違い、想定外の動作をところがあるため)。シーンに展開してからエクスポートしてください")]
|
||||
[LangMsg(Languages.en, "Prefab Asset cannot be exported. Prefab Asset has different behaviour with Scene GameObject. Please put the prefab into the scene")]
|
||||
PREFAB_CANNOT_EXPORT,
|
||||
|
||||
[LangMsg(Languages.ja, "回転・拡大縮小もしくはWeightの無いBlendShapeが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください")]
|
||||
[LangMsg(Languages.en, " Normalization is required. There are nodes (child GameObject) where rotation and scaling or blendshape without bone weight are not default. Please enable PoseFreeze")]
|
||||
ROTATION_OR_SCALEING_INCLUDED_IN_NODE,
|
||||
|
||||
[LangMsg(Languages.ja, "正規化済みです。Setting の PoseFreeze は不要です")]
|
||||
[LangMsg(Languages.en, "Normalization has been done. PoseFreeze is not required")]
|
||||
IS_POSE_FREEZE_DONE,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootに Animator がありません")]
|
||||
[LangMsg(Languages.en, "No Animator in ExportRoot")]
|
||||
NO_ANIMATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "Z+ 向きにしてください")]
|
||||
[LangMsg(Languages.en, "The model needs to face the positive Z-axis")]
|
||||
FACE_Z_POSITIVE_DIRECTION,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator に Avatar がありません")]
|
||||
[LangMsg(Languages.en, "No Avatar in ExportRoot's Animator")]
|
||||
NO_AVATAR_IN_ANIMATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator.Avatar が不正です")]
|
||||
[LangMsg(Languages.en, "Animator.avatar in ExportRoot is not valid")]
|
||||
AVATAR_IS_NOT_VALID,
|
||||
|
||||
[LangMsg(Languages.ja, "ExportRootの Animator.Avatar がヒューマノイドではありません。FBX importer の Rig で設定してください")]
|
||||
[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.en, "Jaw bone is included. It may not what you intended. Please check the humanoid avatar setting screen")]
|
||||
JAW_BONE_IS_INCLUDED,
|
||||
|
||||
[LangMsg(Languages.ja, "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームする")]
|
||||
[LangMsg(Languages.en, "There are bones with the same name in the hierarchy. They will be automatically renamed after export")]
|
||||
DUPLICATE_BONE_NAME_EXISTS,
|
||||
|
||||
[LangMsg(Languages.ja, "VRMBlendShapeProxyが必要です。先にVRMフォーマットに変換してください")]
|
||||
[LangMsg(Languages.en, "VRMBlendShapeProxy is required. Please convert to VRM format first")]
|
||||
NEEDS_VRM_BLENDSHAPE_PROXY,
|
||||
|
||||
[LangMsg(Languages.en, "This model contains vertex color")]
|
||||
[LangMsg(Languages.ja, "ヒエラルキーに含まれる mesh に頂点カラーが含まれている")]
|
||||
VERTEX_COLOR_IS_INCLUDED,
|
||||
|
||||
[LangMsg(Languages.ja, "ヒエラルキーに active なメッシュが含まれていない")]
|
||||
[LangMsg(Languages.en, "No active mesh")]
|
||||
NO_ACTIVE_MESH,
|
||||
|
||||
[LangMsg(Languages.ja, "Standard, Unlit, MToon 以外のマテリアルは、Standard になります")]
|
||||
[LangMsg(Languages.en, "It will export as `Standard` fallback")]
|
||||
UNKNOWN_SHADER,
|
||||
|
||||
[LangMsg(Languages.ja, "名前が長すぎる。リネームしてください: ")]
|
||||
[LangMsg(Languages.en, "FileName is too long: ")]
|
||||
FILENAME_TOO_LONG,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UniGLTF;
|
||||
|
|
@ -36,7 +37,17 @@ namespace VRM
|
|||
throw new Exception();
|
||||
}
|
||||
var context = new VRMImporterContext();
|
||||
context.ParseGlb(File.ReadAllBytes(path.FullPath));
|
||||
|
||||
try
|
||||
{
|
||||
context.ParseGlb(File.ReadAllBytes(path.FullPath));
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
// invalid VRM-0.X.
|
||||
// maybe VRM-1.0.do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
var prefabPath = path.Parent.Child(path.FileNameWithoutExtension + ".prefab");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRM.M17N;
|
||||
using MeshUtility.M17N;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
|
|
@ -54,12 +54,12 @@ namespace VRM
|
|||
|
||||
static string RequiredMessage(string name)
|
||||
{
|
||||
switch (M17N.Getter.Lang)
|
||||
switch (MeshUtility.M17N.Getter.Lang)
|
||||
{
|
||||
case M17N.Languages.ja:
|
||||
case MeshUtility.M17N.Languages.ja:
|
||||
return $"必須項目。{name} を入力してください";
|
||||
|
||||
case M17N.Languages.en:
|
||||
case MeshUtility.M17N.Languages.en:
|
||||
return $"{name} is required";
|
||||
|
||||
default:
|
||||
|
|
@ -153,7 +153,7 @@ namespace VRM
|
|||
|
||||
static string Msg(MessageKeys key)
|
||||
{
|
||||
return M17N.Getter.Msg(key);
|
||||
return MeshUtility.M17N.Getter.Msg(key);
|
||||
}
|
||||
|
||||
bool m_foldoutInfo = true;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MeshUtility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace VRM
|
|||
[TestCase("AliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAlicia", true)]
|
||||
public void DetectFileNameLength(string fileName, bool isIllegal)
|
||||
{
|
||||
var result = VRMExporterValidator.IsFileNameLengthTooLong(fileName);
|
||||
var result = MeshUtility.Validators.NameValidator.IsFileNameLengthTooLong(fileName);
|
||||
Assert.AreEqual(result, isIllegal);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
"VRM",
|
||||
"UniJSON",
|
||||
"UniVRM.Editor",
|
||||
"MeshUtility"
|
||||
"MeshUtility",
|
||||
"MeshUtility.Editor"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"UniJSON",
|
||||
"UniHumanoid",
|
||||
"MeshUtility",
|
||||
"MeshUtility.Editor",
|
||||
"UniUnlit"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
|
|
|
|||
|
|
@ -10,23 +10,6 @@ namespace VRM
|
|||
{
|
||||
public class VRMMaterialExporter : MaterialExporter
|
||||
{
|
||||
public static bool UseUnlit(string shaderName)
|
||||
{
|
||||
switch (shaderName)
|
||||
{
|
||||
case "Unlit/Color":
|
||||
case "Unlit/Texture":
|
||||
case "Unlit/Transparent":
|
||||
case "Unlit/Transparent Cutout":
|
||||
case "UniGLTF/UniUnlit":
|
||||
case "VRM/UnlitTexture":
|
||||
case "VRM/UnlitTransparent":
|
||||
case "VRM/UnlitCutout":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override glTFMaterial CreateMaterial(Material m)
|
||||
{
|
||||
switch (m.shader.name)
|
||||
|
|
@ -118,11 +101,6 @@ namespace VRM
|
|||
}
|
||||
|
||||
#region CreateFromMaterial
|
||||
public static readonly string[] VRMExtensionShaders = new string[]
|
||||
{
|
||||
"VRM/UnlitTransparentZWrite",
|
||||
"VRM/MToon"
|
||||
};
|
||||
|
||||
static readonly string[] TAGS = new string[]{
|
||||
"RenderType",
|
||||
|
|
@ -138,7 +116,7 @@ namespace VRM
|
|||
renderQueue = m.renderQueue,
|
||||
};
|
||||
|
||||
if (!VRMExtensionShaders.Contains(m.shader.name))
|
||||
if (!PreShaderPropExporter.VRMExtensionShaders.Contains(m.shader.name))
|
||||
{
|
||||
material.shader = glTF_VRM_Material.VRM_USE_GLTFSHADER;
|
||||
return material;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using MeshUtility;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ namespace VRM
|
|||
}
|
||||
*/
|
||||
|
||||
public IEnumerable<Validation> Validate()
|
||||
public IEnumerable<Validation> Validate(GameObject _)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Title))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public struct Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// エクスポート可能か否か。
|
||||
/// true のメッセージは警告
|
||||
/// false のメッセージはエラー
|
||||
/// </summary>
|
||||
public readonly bool CanExport;
|
||||
public readonly String Message;
|
||||
|
||||
Validation(bool canExport, string message)
|
||||
{
|
||||
CanExport = canExport;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void DrawGUI()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CanExport)
|
||||
{
|
||||
// warning
|
||||
EditorGUILayout.HelpBox(Message, MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
// error
|
||||
EditorGUILayout.HelpBox(Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static Validation Error(string msg)
|
||||
{
|
||||
return new Validation(false, msg);
|
||||
}
|
||||
|
||||
public static Validation Warning(string msg)
|
||||
{
|
||||
return new Validation(true, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,25 @@
|
|||
|
||||
VRM model's supported shaders in Unity.
|
||||
|
||||
## Import VRMShaders (Unity 2019.3.4f1~)
|
||||
Shader と関連するユーティリティを切り離したパッケージ。
|
||||
|
||||
## 含まれるシェーダー
|
||||
|
||||
### UniUnlit
|
||||
|
||||
* Gltfの Unlit に適合するようにした。Unlit シェーダー
|
||||
|
||||
### MToon
|
||||
|
||||
* https://github.com/Santarh/MToon
|
||||
|
||||
## UniGLTF.ShaderPropExporter.PreShaderPropExporter
|
||||
|
||||
Unityでは、ランタイムにMaterialのPropertyを列挙することができない。
|
||||
Set/Get はできる。
|
||||
事前に一覧を作成するユーティリティ。
|
||||
|
||||
## UPM usage (Unity 2019.3.4f1~)
|
||||
|
||||
`Window` -> `Package Manager` -> `Add package from git URL` and paste `https://github.com/vrm-c/UniVRM.git?path=/Assets/VRMShaders`.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
|
@ -28,6 +27,29 @@ namespace UniGLTF.ShaderPropExporter
|
|||
|
||||
public static partial class PreShaderPropExporter
|
||||
{
|
||||
public static bool UseUnlit(string shaderName)
|
||||
{
|
||||
switch (shaderName)
|
||||
{
|
||||
case "Unlit/Color":
|
||||
case "Unlit/Texture":
|
||||
case "Unlit/Transparent":
|
||||
case "Unlit/Transparent Cutout":
|
||||
case "UniGLTF/UniUnlit":
|
||||
case "VRM/UnlitTexture":
|
||||
case "VRM/UnlitTransparent":
|
||||
case "VRM/UnlitCutout":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static readonly string[] VRMExtensionShaders = new string[]
|
||||
{
|
||||
"VRM/UnlitTransparentZWrite",
|
||||
"VRM/MToon"
|
||||
};
|
||||
|
||||
static Dictionary<string, ShaderProps> m_shaderPropMap;
|
||||
|
||||
public static ShaderProps GetPropsForSupportedShader(string shaderName)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user