using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UniGLTF; using System.IO; #if UNITY_EDITOR using UnityEditor; #endif namespace VRM { [Serializable] public class VRMExportSettings { public GameObject Source; public string Title; public string Author; public bool ForceTPose = true; public bool PoseFreeze = true; public IEnumerable CanExport() { if (Source == null) { yield return "Require source"; yield break; } var animator = Source.GetComponent(); if (animator == null) { yield return "Require animator. "; } else if (animator.avatar == null) { yield return "Require animator.avatar. "; } else if (!animator.avatar.isValid) { yield return "Animator.avatar is not valid. "; } else if (!animator.avatar.isHuman) { yield return "Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. "; } if (string.IsNullOrEmpty(Title)) { yield return "Require Title. "; } if (string.IsNullOrEmpty(Author)) { yield return "Require Author. "; } } public void InitializeFrom(GameObject go) { if (Source == go) return; Source = go; var desc = Source == null ? null : go.GetComponent(); if (desc == null) { ForceTPose = true; PoseFreeze = true; } else { ForceTPose = false; PoseFreeze = false; } var meta = Source == null ? null : go.GetComponent(); if (meta != null && meta.Meta != null) { Title = meta.Meta.Title; Author = meta.Meta.Author; } else { Title = go.name; //Author = ""; } } // // トップレベルのMonoBehaviourを移植する // public static void CopyVRMComponents(GameObject go, GameObject root, Dictionary map) { { // blendshape var src = go.GetComponent(); if (src != null) { var dst = root.AddComponent(); dst.BlendShapeAvatar = src.BlendShapeAvatar; } } { var secondary = go.transform.Find("secondary"); if (secondary == null) { secondary = go.transform; } var dstSecondary = root.transform.Find("secondary"); if (dstSecondary == null) { dstSecondary = new GameObject("secondary").transform; dstSecondary.SetParent(root.transform, false); } // 揺れモノ foreach (var src in go.transform.Traverse().Select(x => x.GetComponent()).Where(x => x != null)) { var dst = map[src.transform]; var dstColliderGroup = dst.gameObject.AddComponent(); dstColliderGroup.Colliders = src.Colliders.Select(y => { var offset = dst.worldToLocalMatrix.MultiplyPoint(src.transform.localToWorldMatrix.MultiplyPoint(y.Offset)); return new VRMSpringBoneColliderGroup.SphereCollider { Offset = offset, Radius = y.Radius }; }).ToArray(); } foreach (var src in go.transform.Traverse().SelectMany(x => x.GetComponents())) { var dst = dstSecondary.gameObject.AddComponent(); dst.m_comment = src.m_comment; dst.RootBones = src.RootBones.Select(x => map[x]).ToList(); if (src.ColliderGroups != null) { dst.ColliderGroups = src.ColliderGroups.Select(x => map[x.transform].GetComponent()).ToArray(); } } } { // meta(obsolete) var src = go.GetComponent(); if (src != null) { src.CopyTo(root); } } { // meta var src = go.GetComponent(); if (src != null) { var dst = root.AddComponent(); dst.Meta = src.Meta; } } { // firstPerson var src = go.GetComponent(); if (src != null) { src.CopyTo(root, map); } } { // lookAt var src = go.GetComponent(); if (src != null) { src.CopyTo(root, map); } } { // humanoid var dst = root.AddComponent(); var src = go.GetComponent(); if (src != null) { dst.Avatar = src.Avatar; dst.Description = src.Description; } else { var animator = go.GetComponent(); if (animator != null) { dst.Avatar = animator.avatar; } } } } public static bool IsPrefab(GameObject go) { return go.scene.name == null; } #if UNITY_EDITOR struct RecordDisposer : IDisposable { public RecordDisposer(UnityEngine.Object[] objects, string msg) { Undo.RecordObjects(objects, msg); } public void Dispose() { Undo.PerformUndo(); } } public void Export(string path) { List destroy = new List(); try { Export(path, destroy); } finally { foreach (var x in destroy) { Debug.LogFormat("destroy: {0}", x.name); GameObject.DestroyImmediate(x); } } } void Export(string path, List destroy) { var target = Source; if (IsPrefab(target)) { using (new RecordDisposer(Source.transform.Traverse().ToArray(), "before normalize")) { target = GameObject.Instantiate(target); destroy.Add(target); } } if (PoseFreeze) { using (new RecordDisposer(target.transform.Traverse().ToArray(), "before normalize")) { var map = new Dictionary(); var copy = BoneNormalizer.Execute(target, map, ForceTPose); CopyVRMComponents(target, copy, map); target = copy; destroy.Add(target); } } { var sw = System.Diagnostics.Stopwatch.StartNew(); var vrm = VRMExporter.Export(target); vrm.extensions.VRM.meta.title = Title; vrm.extensions.VRM.meta.author = Author; var bytes = vrm.ToGlbBytes(); File.WriteAllBytes(path, bytes); Debug.LogFormat("Export elapsed {0}", sw.Elapsed); } if (path.StartsWithUnityAssetPath()) { AssetDatabase.ImportAsset(path.ToUnityRelativePath()); } } #endif } }