From 973eabb7218c186552132dfbc3f0c6408c9c023d Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 19:39:14 +0900 Subject: [PATCH 01/13] separate VRMExporterWizard.cs --- .../UniVRM/Editor/Format/VRMExporterMenu.cs | 86 +----------------- .../Editor/Format/VRMExporterMenu.cs.meta | 5 +- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 90 +++++++++++++++++++ .../Editor/Format/VRMExporterWizard.cs.meta | 12 +++ 4 files changed, 105 insertions(+), 88 deletions(-) create mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs create mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs index 92efb5c297..38bd6da806 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs @@ -1,93 +1,9 @@ -using System.IO; -using System.Text; -using UnityEditor; +using UnityEditor; using UnityEngine; namespace VRM { - public class VRMExporterWizard : ScriptableWizard - { - const string EXTENSION = ".vrm"; - - VRMMeta m_meta; - - private static string m_lastExportDir; - - public VRMExportSettings m_settings = new VRMExportSettings(); - - public static void CreateWizard() - { - var wiz = ScriptableWizard.DisplayWizard( - "VRM Exporter", "Export"); - var go = Selection.activeObject as GameObject; - - // update checkbox - wiz.m_settings.InitializeFrom(go); - - wiz.OnWizardUpdate(); - } - - void OnEnable() - { - // Debug.Log("OnEnable"); - Undo.willFlushUndoRecord += OnWizardUpdate; - } - - void OnDisable() - { - // Debug.Log("OnDisable"); - Undo.willFlushUndoRecord -= OnWizardUpdate; - } - - void OnWizardCreate() - { - string directory; - if (string.IsNullOrEmpty(m_lastExportDir)) - directory = Directory.GetParent(Application.dataPath).ToString(); - else - directory = m_lastExportDir; - - // save dialog - var path = EditorUtility.SaveFilePanel( - "Save vrm", - directory, - m_settings.Source.name + EXTENSION, - EXTENSION.Substring(1)); - if (string.IsNullOrEmpty(path)) - { - return; - } - m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/"); - - // export - m_settings.Export(path); - } - - void OnWizardUpdate() - { - isValid = true; - var helpBuilder = new StringBuilder(); - var errorBuilder = new StringBuilder(); - - foreach (var validation in m_settings.CanExport()) - { - if (!validation.CanExport) - { - isValid = false; - errorBuilder.Append(validation.Message); - } - else - { - helpBuilder.AppendLine(validation.Message); - } - } - - helpString = helpBuilder.ToString(); - errorString = errorBuilder.ToString(); - } - } - public static class VRMExporterMenu { const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export humanoid"; diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta index ee2067f06f..5100b682ae 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta @@ -1,8 +1,7 @@ fileFormatVersion: 2 -guid: a8e41aa30fcc76e43ad8588aef8572ea -timeCreated: 1520491195 -licenseType: Free +guid: a1429b9028f33544e94aa367c2acb7fb MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs new file mode 100644 index 0000000000..4e5e90243a --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -0,0 +1,90 @@ +using System.IO; +using System.Text; +using UnityEditor; +using UnityEngine; + + +namespace VRM +{ + public class VRMExporterWizard : ScriptableWizard + { + const string EXTENSION = ".vrm"; + + VRMMeta m_meta; + + private static string m_lastExportDir; + + public VRMExportSettings m_settings = new VRMExportSettings(); + + public static void CreateWizard() + { + var wiz = ScriptableWizard.DisplayWizard( + "VRM Exporter", "Export"); + var go = Selection.activeObject as GameObject; + + // update checkbox + wiz.m_settings.InitializeFrom(go); + + wiz.OnWizardUpdate(); + } + + void OnEnable() + { + // Debug.Log("OnEnable"); + Undo.willFlushUndoRecord += OnWizardUpdate; + } + + void OnDisable() + { + // Debug.Log("OnDisable"); + Undo.willFlushUndoRecord -= OnWizardUpdate; + } + + void OnWizardCreate() + { + string directory; + if (string.IsNullOrEmpty(m_lastExportDir)) + directory = Directory.GetParent(Application.dataPath).ToString(); + else + directory = m_lastExportDir; + + // save dialog + var path = EditorUtility.SaveFilePanel( + "Save vrm", + directory, + m_settings.Source.name + EXTENSION, + EXTENSION.Substring(1)); + if (string.IsNullOrEmpty(path)) + { + return; + } + m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/"); + + // export + m_settings.Export(path); + } + + void OnWizardUpdate() + { + isValid = true; + var helpBuilder = new StringBuilder(); + var errorBuilder = new StringBuilder(); + + foreach (var validation in m_settings.CanExport()) + { + if (!validation.CanExport) + { + isValid = false; + errorBuilder.Append(validation.Message); + } + else + { + helpBuilder.AppendLine(validation.Message); + } + } + + helpString = helpBuilder.ToString(); + errorString = errorBuilder.ToString(); + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta new file mode 100644 index 0000000000..ee2067f06f --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a8e41aa30fcc76e43ad8588aef8572ea +timeCreated: 1520491195 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 3a216e634c008e02916348c16994b103b8a56b6c Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 19:56:05 +0900 Subject: [PATCH 02/13] Remove not used --- .../Editor/Format/VRMExportObjectEditor.cs | 104 ------------------ .../Format/VRMExportObjectEditor.cs.meta | 12 -- .../UniVRM/Scripts/Format/VRMExportObject.cs | 12 -- .../Scripts/Format/VRMExportObject.cs.meta | 12 -- 4 files changed, 140 deletions(-) delete mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs delete mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs.meta delete mode 100644 Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs delete mode 100644 Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs deleted file mode 100644 index a5c76971f7..0000000000 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using UnityEditor; -using UnityEngine; - - -namespace VRM -{ - [CustomEditor(typeof(VRMExportObject))] - public class VRMExportObjectEditor : Editor - { - SerializedProperty m_settings; - VRMExportObject m_target; - - void OnEnable() - { - m_target = target as VRMExportObject; - m_settings = serializedObject.FindProperty("Settings"); - } - - public override void OnInspectorGUI() - { - // - // Editor - // - serializedObject.Update(); - - var before = m_target.Settings.Source; - - EditorGUILayout.PropertyField(m_settings, true); - serializedObject.ApplyModifiedProperties(); - - // - // - // - var after = m_target.Settings.Source; - if (before != after) - { - m_target.Settings.InitializeFrom(after as GameObject); - } - - bool canExport = m_target.Settings.Source != null; - foreach (var validation in m_target.Settings.CanExport()) - { - if (!validation.CanExport) - { - canExport = false; - } - EditorGUILayout.HelpBox(validation.Message, validation.CanExport ? MessageType.Warning : MessageType.Error); - } - - if (canExport) - { - if (GUILayout.Button("Export")) - { - var path = EditorUtility.SaveFilePanel( - "Save vrm", - null,//Dir, - m_target.Settings.Source.name + ".vrm", - "vrm"); - if (!string.IsNullOrEmpty(path)) - { - var target = m_target; - EditorApplication.delayCall += () => - { - target.Settings.Export(path); - }; - } - } - } - } - - class DisposableInstance : IDisposable - { - GameObject m_go; - public GameObject GameObject - { - get - { - return m_go; - } - } - - public DisposableInstance(GameObject prefab) - { - m_go = GameObject.Instantiate(prefab); - } - - public void Dispose() - { - if (m_go != null) - { - if (Application.isPlaying) - { - GameObject.Destroy(m_go); - } - else - { - GameObject.DestroyImmediate(m_go); - } - } - } - } - } -} diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs.meta deleted file mode 100644 index 9a7ef2be7b..0000000000 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 01708ecf1aa756948be6996d987684a7 -timeCreated: 1532063961 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs b/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs deleted file mode 100644 index 360c47518b..0000000000 --- a/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UnityEngine; - - -namespace VRM -{ - [CreateAssetMenu(menuName = "VRM/ExportObject")] - public class VRMExportObject : ScriptableObject - { - [SerializeField] - public VRMExportSettings Settings = new VRMExportSettings(); - } -} diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs.meta b/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs.meta deleted file mode 100644 index 90b6df4554..0000000000 --- a/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: f8ed5cb82dd13bf43a1556b24a6d13a0 -timeCreated: 1532063757 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From e4b5f19c1f01a2b0735cc8ff3bce3567315c1e33 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 19:59:51 +0900 Subject: [PATCH 03/13] move VRMExportSettings.cs to Editor --- .../{Scripts => Editor}/Format/VRMExportSettings.cs | 7 ++----- .../{Scripts => Editor}/Format/VRMExportSettings.cs.meta | 0 Assets/VRM/UniVRM/Editor/UniVRM.Editor.asmdef | 9 +++++++-- 3 files changed, 9 insertions(+), 7 deletions(-) rename Assets/VRM/UniVRM/{Scripts => Editor}/Format/VRMExportSettings.cs (99%) rename Assets/VRM/UniVRM/{Scripts => Editor}/Format/VRMExportSettings.cs.meta (100%) diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs similarity index 99% rename from Assets/VRM/UniVRM/Scripts/Format/VRMExportSettings.cs rename to Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 0416aac328..511650f417 100644 --- a/Assets/VRM/UniVRM/Scripts/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -1,14 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using UnityEngine; -using UniGLTF; using System.IO; -#if UNITY_EDITOR +using UniGLTF; +using UnityEngine; using UnityEditor; -#endif - namespace VRM { diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExportSettings.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs.meta similarity index 100% rename from Assets/VRM/UniVRM/Scripts/Format/VRMExportSettings.cs.meta rename to Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/UniVRM.Editor.asmdef b/Assets/VRM/UniVRM/Editor/UniVRM.Editor.asmdef index 35b3e62547..b0fde8cc95 100644 --- a/Assets/VRM/UniVRM/Editor/UniVRM.Editor.asmdef +++ b/Assets/VRM/UniVRM/Editor/UniVRM.Editor.asmdef @@ -2,12 +2,17 @@ "name": "UniVRM.Editor", "references": [ "VRM", - "UniJSON" + "UniJSON", + "UniHumanoid" ], "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], "excludePlatforms": [], - "allowUnsafeCode": false + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] } \ No newline at end of file From 1669fb9b74213a8f93e84a863a79c499321b8d1f Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 20:20:49 +0900 Subject: [PATCH 04/13] separate RecordDisposer.cs and VRMEditorExporter.cs --- .../UniVRM/Editor/Format/RecordDisposer.cs | 147 ++++++++ .../Editor/Format/RecordDisposer.cs.meta | 11 + .../UniVRM/Editor/Format/VRMEditorExporter.cs | 165 +++++++++ .../Editor/Format/VRMEditorExporter.cs.meta | 11 + .../UniVRM/Editor/Format/VRMExportSettings.cs | 319 +----------------- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 4 +- .../Format/VRMHumanoidNormalizerMenu.cs | 4 +- 7 files changed, 351 insertions(+), 310 deletions(-) create mode 100644 Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs create mode 100644 Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta create mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs create mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs new file mode 100644 index 0000000000..a824ac8bc0 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace VRM +{ + public struct RecordDisposer : IDisposable + { + int _group; + public RecordDisposer(UnityEngine.Object[] objects, string msg) + { + Undo.IncrementCurrentGroup(); + _group = Undo.GetCurrentGroup(); + Undo.RecordObjects(objects, msg); + } + + public void Dispose() + { + Undo.RevertAllDownToGroup(_group); + } + + /// + /// VRMを構成するコンポーネントをコピーする。 + /// + /// コピー元 + /// コピー先 + /// コピー元とコピー先の対応関係 + 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.GetComponentsInChildren()) + { + 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.GetComponentsInChildren()) + { + // Copy VRMSpringBone + var dst = dstSecondary.gameObject.AddComponent(); + dst.m_comment = src.m_comment; + dst.m_stiffnessForce = src.m_stiffnessForce; + dst.m_gravityPower = src.m_gravityPower; + dst.m_gravityDir = src.m_gravityDir; + dst.m_dragForce = src.m_dragForce; + if (src.m_center != null) + { + dst.m_center = map[src.m_center]; + } + + dst.RootBones = src.RootBones.Select(x => map[x]).ToList(); + dst.m_hitRadius = src.m_hitRadius; + if (src.ColliderGroups != null) + { + dst.ColliderGroups = src.ColliderGroups + .Select(x => map[x.transform].GetComponent()).ToArray(); + } + } + } + +#pragma warning disable 0618 + { + // meta(obsolete) + var src = go.GetComponent(); + if (src != null) + { + src.CopyTo(root); + } + } +#pragma warning restore 0618 + + { + // 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); + } + } + + { + // 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; + } + } + } + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta new file mode 100644 index 0000000000..5cf6bf1e3f --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b304ed2aeece5a54191a5a8b69d9d113 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs new file mode 100644 index 0000000000..0fd5b15a28 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UniGLTF; +using UnityEditor; +using UnityEngine; + +namespace VRM +{ + public static class VRMEditorExporter + { + public static void Export(string path, VRMExportSettings settings) + { + List destroy = new List(); + try + { + Export(path, settings, destroy); + } + finally + { + foreach (var x in destroy) + { + Debug.LogFormat("destroy: {0}", x.name); + GameObject.DestroyImmediate(x); + } + } + } + + static bool IsPrefab(GameObject go) + { + return !go.scene.IsValid(); + } + + + static void Export(string path, VRMExportSettings settings, List destroy) + { + var target = settings.Source; + if (IsPrefab(target)) + { + using (new RecordDisposer(settings.Source.transform.Traverse().ToArray(), "before normalize")) + { + target = GameObject.Instantiate(target); + destroy.Add(target); + } + } + + if (settings.PoseFreeze) + { + using (new RecordDisposer(target.transform.Traverse().ToArray(), "before normalize")) + { + var normalized = BoneNormalizer.Execute(target, settings.ForceTPose, false); + RecordDisposer.CopyVRMComponents(target, normalized.Root, normalized.BoneMap); + target = normalized.Root; + destroy.Add(target); + } + } + + // remove unused blendShape + if (settings.ReduceBlendshapeSize) + { + var proxy = target.GetComponent(); + + // 元のBlendShapeClipに変更を加えないように複製 + var copyBlendShapeAvatar = GameObject.Instantiate(proxy.BlendShapeAvatar); + var copyBlendShapClips = new List(); + + foreach (var clip in proxy.BlendShapeAvatar.Clips) + { + copyBlendShapClips.Add(GameObject.Instantiate(clip)); + } + + var skinnedMeshRenderers = target.GetComponentsInChildren(); + + var names = new Dictionary(); + var vs = new Dictionary(); + var ns = new Dictionary(); + var ts = new Dictionary(); + + foreach (SkinnedMeshRenderer smr in skinnedMeshRenderers) + { + Mesh mesh = smr.sharedMesh; + if (mesh == null) continue; + if (mesh.blendShapeCount == 0) continue; + + var copyMesh = mesh.Copy(true); + var vCount = copyMesh.vertexCount; + names.Clear(); + + vs.Clear(); + ns.Clear(); + ts.Clear(); + + var usedBlendshapeIndexArray = copyBlendShapClips + .SelectMany(clip => clip.Values) + .Where(val => target.transform.Find(val.RelativePath) == smr.transform) + .Select(val => val.Index) + .Distinct() + .ToArray(); + + foreach (var i in usedBlendshapeIndexArray) + { + var name = copyMesh.GetBlendShapeName(i); + var vertices = new Vector3[vCount]; + var normals = new Vector3[vCount]; + var tangents = new Vector3[vCount]; + copyMesh.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); + + names.Add(i, name); + vs.Add(i, vertices); + ns.Add(i, normals); + ts.Add(i, tangents); + } + + copyMesh.ClearBlendShapes(); + + foreach (var i in usedBlendshapeIndexArray) + { + copyMesh.AddBlendShapeFrame(names[i], 100f, vs[i], ns[i], ts[i]); + } + + var indexMapper = usedBlendshapeIndexArray + .Select((x, i) => new { x, i }) + .ToDictionary(pair => pair.x, pair => pair.i); + + foreach (var clip in copyBlendShapClips) + { + for (var i = 0; i < clip.Values.Length; ++i) + { + var value = clip.Values[i]; + if (target.transform.Find(value.RelativePath) != smr.transform) continue; + value.Index = indexMapper[value.Index]; + clip.Values[i] = value; + } + } + + copyBlendShapeAvatar.Clips = copyBlendShapClips; + + proxy.BlendShapeAvatar = copyBlendShapeAvatar; + + smr.sharedMesh = copyMesh; + } + } + + { + var sw = System.Diagnostics.Stopwatch.StartNew(); + var vrm = VRMExporter.Export(target, settings.ReduceBlendshapeSize); + vrm.extensions.VRM.meta.title = settings.Title; + vrm.extensions.VRM.meta.version = settings.Version; + vrm.extensions.VRM.meta.author = settings.Author; + vrm.extensions.VRM.meta.contactInformation = settings.ContactInformation; + vrm.extensions.VRM.meta.reference = settings.Reference; + + var bytes = vrm.ToGlbBytes(settings.UseExperimentalExporter ? SerializerTypes.Generated : SerializerTypes.UniJSON); + File.WriteAllBytes(path, bytes); + Debug.LogFormat("Export elapsed {0}", sw.Elapsed); + } + + if (path.StartsWithUnityAssetPath()) + { + AssetDatabase.ImportAsset(path.ToUnityRelativePath()); + } + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta new file mode 100644 index 0000000000..f6863a9572 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cea266830a7f57843bb928d0ea37bcbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 511650f417..c6ffe529dc 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.IO; using UniGLTF; using UnityEngine; -using UnityEditor; namespace VRM @@ -54,7 +52,19 @@ public static Validation Warning(string msg) } } - public IEnumerable CanExport() + //ここで重複ボーン名のチェックをする + bool DuplicateBoneNameExists() + { + var bones = Source.transform.Traverse().ToArray(); + var duplicates = bones + .GroupBy(p => p.name) + .Where(g => g.Count() > 1) + .Select(g => g.Key); + + return (duplicates.Any()); + } + + public IEnumerable Validate() { if (Source == null) { @@ -172,308 +182,5 @@ public void InitializeFrom(GameObject go) Version = "0.0"; } } - - // - // トップレベルの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())) - { - // Copy VRMSpringBone - var dst = dstSecondary.gameObject.AddComponent(); - dst.m_comment = src.m_comment; - dst.m_stiffnessForce = src.m_stiffnessForce; - dst.m_gravityPower = src.m_gravityPower; - dst.m_gravityDir = src.m_gravityDir; - dst.m_dragForce = src.m_dragForce; - if (src.m_center != null) - { - dst.m_center = map[src.m_center]; - } - - dst.RootBones = src.RootBones.Select(x => map[x]).ToList(); - dst.m_hitRadius = src.m_hitRadius; - if (src.ColliderGroups != null) - { - dst.ColliderGroups = src.ColliderGroups - .Select(x => map[x.transform].GetComponent()).ToArray(); - } - } - } - -#pragma warning disable 0618 - { - // meta(obsolete) - var src = go.GetComponent(); - if (src != null) - { - src.CopyTo(root); - } - } -#pragma warning restore 0618 - - { - // 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); - } - } - - { - // 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.IsValid(); - } - -#if UNITY_EDITOR - public struct RecordDisposer : IDisposable - { - int _group; - public RecordDisposer(UnityEngine.Object[] objects, string msg) - { - Undo.IncrementCurrentGroup(); - _group = Undo.GetCurrentGroup(); - Undo.RecordObjects(objects, msg); - } - - public void Dispose() - { - Undo.RevertAllDownToGroup(_group); - } - } - - 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 normalized = BoneNormalizer.Execute(target, ForceTPose, false); - CopyVRMComponents(target, normalized.Root, normalized.BoneMap); - target = normalized.Root; - destroy.Add(target); - } - } - - // remove unused blendShape - if (ReduceBlendshapeSize) - { - var proxy = target.GetComponent(); - - // 元のBlendShapeClipに変更を加えないように複製 - var copyBlendShapeAvatar = GameObject.Instantiate(proxy.BlendShapeAvatar); - var copyBlendShapClips = new List(); - - foreach (var clip in proxy.BlendShapeAvatar.Clips) - { - copyBlendShapClips.Add(GameObject.Instantiate(clip)); - } - - var skinnedMeshRenderers = target.GetComponentsInChildren(); - - var names = new Dictionary(); - var vs = new Dictionary(); - var ns = new Dictionary(); - var ts = new Dictionary(); - - foreach (SkinnedMeshRenderer smr in skinnedMeshRenderers) - { - Mesh mesh = smr.sharedMesh; - if (mesh == null) continue; - if (mesh.blendShapeCount == 0) continue; - - var copyMesh = mesh.Copy(true); - var vCount = copyMesh.vertexCount; - names.Clear(); - - vs.Clear(); - ns.Clear(); - ts.Clear(); - - var usedBlendshapeIndexArray = copyBlendShapClips - .SelectMany(clip => clip.Values) - .Where(val => target.transform.Find(val.RelativePath) == smr.transform) - .Select(val => val.Index) - .Distinct() - .ToArray(); - - foreach (var i in usedBlendshapeIndexArray) - { - var name = copyMesh.GetBlendShapeName(i); - var vertices = new Vector3[vCount]; - var normals = new Vector3[vCount]; - var tangents = new Vector3[vCount]; - copyMesh.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); - - names.Add(i, name); - vs.Add(i, vertices); - ns.Add(i, normals); - ts.Add(i, tangents); - } - - copyMesh.ClearBlendShapes(); - - foreach (var i in usedBlendshapeIndexArray) - { - copyMesh.AddBlendShapeFrame(names[i], 100f, vs[i], ns[i], ts[i]); - } - - var indexMapper = usedBlendshapeIndexArray - .Select((x, i) => new { x, i }) - .ToDictionary(pair => pair.x, pair => pair.i); - - foreach (var clip in copyBlendShapClips) - { - for (var i = 0; i < clip.Values.Length; ++i) - { - var value = clip.Values[i]; - if (target.transform.Find(value.RelativePath) != smr.transform) continue; - value.Index = indexMapper[value.Index]; - clip.Values[i] = value; - } - } - - copyBlendShapeAvatar.Clips = copyBlendShapClips; - - proxy.BlendShapeAvatar = copyBlendShapeAvatar; - - smr.sharedMesh = copyMesh; - } - } - - { - var sw = System.Diagnostics.Stopwatch.StartNew(); - var vrm = VRMExporter.Export(target, ReduceBlendshapeSize); - vrm.extensions.VRM.meta.title = Title; - vrm.extensions.VRM.meta.version = Version; - vrm.extensions.VRM.meta.author = Author; - vrm.extensions.VRM.meta.contactInformation = ContactInformation; - vrm.extensions.VRM.meta.reference = Reference; - - - var bytes = vrm.ToGlbBytes(UseExperimentalExporter ? SerializerTypes.Generated : SerializerTypes.UniJSON); - File.WriteAllBytes(path, bytes); - Debug.LogFormat("Export elapsed {0}", sw.Elapsed); - } - - - if (path.StartsWithUnityAssetPath()) - { - AssetDatabase.ImportAsset(path.ToUnityRelativePath()); - } - } -#endif - - //ここで重複ボーン名のチェックをする - bool DuplicateBoneNameExists() - { - var bones = Source.transform.Traverse().ToArray(); - var duplicates = bones - .GroupBy(p => p.name) - .Where(g => g.Count() > 1) - .Select(g => g.Key); - - return (duplicates.Any()); - } } } \ No newline at end of file diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 4e5e90243a..90b1faa123 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -61,7 +61,7 @@ void OnWizardCreate() m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/"); // export - m_settings.Export(path); + VRMEditorExporter.Export(path, m_settings); } void OnWizardUpdate() @@ -70,7 +70,7 @@ void OnWizardUpdate() var helpBuilder = new StringBuilder(); var errorBuilder = new StringBuilder(); - foreach (var validation in m_settings.CanExport()) + foreach (var validation in m_settings.Validate()) { if (!validation.CanExport) { diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs index 400dbb2f7e..fbb6d8a320 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs @@ -49,10 +49,10 @@ private static void ExportFromMenu() var go = Selection.activeObject as GameObject; GameObject normalizedRoot = null; - using (new VRMExportSettings.RecordDisposer(go.transform.Traverse().ToArray(), "before normalize")) + using (new RecordDisposer(go.transform.Traverse().ToArray(), "before normalize")) { var normalized = BoneNormalizer.Execute(go, true, false); - VRMExportSettings.CopyVRMComponents(go, normalized.Root, normalized.BoneMap); + RecordDisposer.CopyVRMComponents(go, normalized.Root, normalized.BoneMap); normalizedRoot = normalized.Root; } Selection.activeGameObject = normalizedRoot; From 58e26ed07dca6e7cb85464b72c2206cfc9706ce5 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 20:33:12 +0900 Subject: [PATCH 05/13] comment --- .../UniVRM/Editor/Format/VRMExportSettings.cs | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index c6ffe529dc..2d8d3fa3be 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -10,28 +10,67 @@ namespace VRM [Serializable] public class VRMExportSettings { + /// + /// エクスポート対象 + /// public GameObject Source; + #region Meta + /// + /// エクスポート名 + /// public string Title; + /// + /// エクスポートバージョン(エクスポートするModelのバージョン) + /// public string Version; + /// + /// 作者 + /// public string Author; + /// + /// 作者連絡先 + /// public string ContactInformation; + /// + /// 作品引用 + /// public string Reference; + #endregion + #region Settings + /// + /// エクスポート時に強制的にT-Pose化する + /// public bool ForceTPose = true; + /// + /// エクスポート時にヒエラルキーの正規化を実施する + /// public bool PoseFreeze = true; + /// + /// エクスポート時に新しいJsonSerializerを使う + /// public bool UseExperimentalExporter = false; + /// + /// エクスポート時にBlendShapeを削減する + /// public bool ReduceBlendshapeSize = false; + #endregion public struct Validation { + /// + /// エクスポート可能か否か。 + /// true のメッセージは警告 + /// false のメッセージはエラー + /// public readonly bool CanExport; public readonly String Message; @@ -52,7 +91,10 @@ public static Validation Warning(string msg) } } - //ここで重複ボーン名のチェックをする + /// + /// ボーン名の重複を確認 + /// + /// bool DuplicateBoneNameExists() { var bones = Source.transform.Traverse().ToArray(); @@ -64,6 +106,10 @@ bool DuplicateBoneNameExists() return (duplicates.Any()); } + /// + /// エクスポート可能か検証する + /// + /// public IEnumerable Validate() { if (Source == null) @@ -150,23 +196,35 @@ public IEnumerable Validate() } } + /// + /// 対象のモデルからMeta情報を取得し、エクスポート設定を初期する + /// + /// public void InitializeFrom(GameObject go) { if (Source == go) return; Source = go; + // + // initialize + // var desc = Source == null ? null : go.GetComponent(); if (desc == null) { + // 初回のVRMエクスポートとみなす ForceTPose = true; PoseFreeze = true; } else { + // すでに正規化済みとみなす ForceTPose = false; PoseFreeze = false; } + // + // Meta + // var meta = Source == null ? null : go.GetComponent(); if (meta != null && meta.Meta != null) { From cc7c60e61b5f1e1644af34c1092497b73cddff59 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 21:07:00 +0900 Subject: [PATCH 06/13] CopyBlendShapeAvatar --- .../UniVRM/Editor/Format/VRMEditorExporter.cs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index 0fd5b15a28..ce2a1675a1 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -32,6 +32,21 @@ static bool IsPrefab(GameObject go) return !go.scene.IsValid(); } + /// + /// DeepCopy + /// + /// + /// + static BlendShapeAvatar CopyBlendShapeAvatar(BlendShapeAvatar src) + { + var avatar = GameObject.Instantiate(src); + avatar.Clips = new List(); + foreach (var clip in src.Clips) + { + avatar.Clips.Add(GameObject.Instantiate(clip)); + } + return avatar; + } static void Export(string path, VRMExportSettings settings, List destroy) { @@ -62,13 +77,7 @@ static void Export(string path, VRMExportSettings settings, List des var proxy = target.GetComponent(); // 元のBlendShapeClipに変更を加えないように複製 - var copyBlendShapeAvatar = GameObject.Instantiate(proxy.BlendShapeAvatar); - var copyBlendShapClips = new List(); - - foreach (var clip in proxy.BlendShapeAvatar.Clips) - { - copyBlendShapClips.Add(GameObject.Instantiate(clip)); - } + var copyBlendShapeAvatar = CopyBlendShapeAvatar(proxy.BlendShapeAvatar); var skinnedMeshRenderers = target.GetComponentsInChildren(); @@ -91,7 +100,7 @@ static void Export(string path, VRMExportSettings settings, List des ns.Clear(); ts.Clear(); - var usedBlendshapeIndexArray = copyBlendShapClips + var usedBlendshapeIndexArray = copyBlendShapeAvatar.Clips .SelectMany(clip => clip.Values) .Where(val => target.transform.Find(val.RelativePath) == smr.transform) .Select(val => val.Index) @@ -123,7 +132,7 @@ static void Export(string path, VRMExportSettings settings, List des .Select((x, i) => new { x, i }) .ToDictionary(pair => pair.x, pair => pair.i); - foreach (var clip in copyBlendShapClips) + foreach (var clip in copyBlendShapeAvatar.Clips) { for (var i = 0; i < clip.Values.Length; ++i) { @@ -134,8 +143,6 @@ static void Export(string path, VRMExportSettings settings, List des } } - copyBlendShapeAvatar.Clips = copyBlendShapClips; - proxy.BlendShapeAvatar = copyBlendShapeAvatar; smr.sharedMesh = copyMesh; From 0d35c6df0dfb55f259088cf048dd5354e95b5ada Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 21:31:23 +0900 Subject: [PATCH 07/13] cleanup --- .../UniVRM/Editor/Format/VRMEditorExporter.cs | 132 +++++++++--------- .../UniVRM/Editor/Format/VRMExportSettings.cs | 2 +- 2 files changed, 66 insertions(+), 68 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index ce2a1675a1..5c2474ebdb 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -10,6 +10,11 @@ namespace VRM { public static class VRMEditorExporter { + /// + /// Editor向けのエクスポート処理 + /// + /// 出力先 + /// エクスポート設定 public static void Export(string path, VRMExportSettings settings) { List destroy = new List(); @@ -48,6 +53,57 @@ static BlendShapeAvatar CopyBlendShapeAvatar(BlendShapeAvatar src) return avatar; } + /// + /// 使用されない BlendShape を間引いた Mesh を作成して置き換える + /// + /// + /// + static void ReplaceMesh(GameObject target, SkinnedMeshRenderer smr, BlendShapeAvatar copyBlendShapeAvatar) + { + Mesh mesh = smr.sharedMesh; + if (mesh == null) return; + if (mesh.blendShapeCount == 0) return; + + // Mesh から BlendShapeClip からの参照がある blendShape の index を集める + var usedBlendshapeIndexArray = copyBlendShapeAvatar.Clips + .SelectMany(clip => clip.Values) + .Where(val => target.transform.Find(val.RelativePath) == smr.transform) + .Select(val => val.Index) + .Distinct() + .ToArray(); + + var copyMesh = mesh.Copy(copyBlendShape: false); + // 使われている BlendShape だけをコピーする + foreach (var i in usedBlendshapeIndexArray) + { + var name = copyMesh.GetBlendShapeName(i); + var vCount = copyMesh.vertexCount; + var vertices = new Vector3[vCount]; + var normals = new Vector3[vCount]; + var tangents = new Vector3[vCount]; + mesh.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); + copyMesh.AddBlendShapeFrame(name, 100f, vertices, normals, tangents); + } + + // BlendShapeClip の BlendShapeIndex を更新する(前に詰める) + var indexMapper = usedBlendshapeIndexArray + .Select((x, i) => new { x, i }) + .ToDictionary(pair => pair.x, pair => pair.i); + foreach (var clip in copyBlendShapeAvatar.Clips) + { + for (var i = 0; i < clip.Values.Length; ++i) + { + var value = clip.Values[i]; + if (target.transform.Find(value.RelativePath) != smr.transform) continue; + value.Index = indexMapper[value.Index]; + clip.Values[i] = value; + } + } + + // mesh を置き換える + smr.sharedMesh = copyMesh; + } + static void Export(string path, VRMExportSettings settings, List destroy) { var target = settings.Source; @@ -60,6 +116,7 @@ static void Export(string path, VRMExportSettings settings, List des } } + // 正規化 if (settings.PoseFreeze) { using (new RecordDisposer(target.transform.Traverse().ToArray(), "before normalize")) @@ -71,84 +128,24 @@ static void Export(string path, VRMExportSettings settings, List des } } - // remove unused blendShape + // BlendShape削減 if (settings.ReduceBlendshapeSize) { + // remove unused blendShape var proxy = target.GetComponent(); // 元のBlendShapeClipに変更を加えないように複製 var copyBlendShapeAvatar = CopyBlendShapeAvatar(proxy.BlendShapeAvatar); + proxy.BlendShapeAvatar = copyBlendShapeAvatar; - var skinnedMeshRenderers = target.GetComponentsInChildren(); - - var names = new Dictionary(); - var vs = new Dictionary(); - var ns = new Dictionary(); - var ts = new Dictionary(); - - foreach (SkinnedMeshRenderer smr in skinnedMeshRenderers) + foreach (SkinnedMeshRenderer smr in target.GetComponentsInChildren()) { - Mesh mesh = smr.sharedMesh; - if (mesh == null) continue; - if (mesh.blendShapeCount == 0) continue; - - var copyMesh = mesh.Copy(true); - var vCount = copyMesh.vertexCount; - names.Clear(); - - vs.Clear(); - ns.Clear(); - ts.Clear(); - - var usedBlendshapeIndexArray = copyBlendShapeAvatar.Clips - .SelectMany(clip => clip.Values) - .Where(val => target.transform.Find(val.RelativePath) == smr.transform) - .Select(val => val.Index) - .Distinct() - .ToArray(); - - foreach (var i in usedBlendshapeIndexArray) - { - var name = copyMesh.GetBlendShapeName(i); - var vertices = new Vector3[vCount]; - var normals = new Vector3[vCount]; - var tangents = new Vector3[vCount]; - copyMesh.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); - - names.Add(i, name); - vs.Add(i, vertices); - ns.Add(i, normals); - ts.Add(i, tangents); - } - - copyMesh.ClearBlendShapes(); - - foreach (var i in usedBlendshapeIndexArray) - { - copyMesh.AddBlendShapeFrame(names[i], 100f, vs[i], ns[i], ts[i]); - } - - var indexMapper = usedBlendshapeIndexArray - .Select((x, i) => new { x, i }) - .ToDictionary(pair => pair.x, pair => pair.i); - - foreach (var clip in copyBlendShapeAvatar.Clips) - { - for (var i = 0; i < clip.Values.Length; ++i) - { - var value = clip.Values[i]; - if (target.transform.Find(value.RelativePath) != smr.transform) continue; - value.Index = indexMapper[value.Index]; - clip.Values[i] = value; - } - } - - proxy.BlendShapeAvatar = copyBlendShapeAvatar; - - smr.sharedMesh = copyMesh; + // 未使用のBlendShapeを間引く + ReplaceMesh(target, smr, copyBlendShapeAvatar); } } + // 出力 { var sw = System.Diagnostics.Stopwatch.StartNew(); var vrm = VRMExporter.Export(target, settings.ReduceBlendshapeSize); @@ -165,6 +162,7 @@ static void Export(string path, VRMExportSettings settings, List des if (path.StartsWithUnityAssetPath()) { + // 出力ファイルのインポートを発動 AssetDatabase.ImportAsset(path.ToUnityRelativePath()); } } diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 2d8d3fa3be..588061205a 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -59,7 +59,7 @@ public class VRMExportSettings public bool UseExperimentalExporter = false; /// - /// エクスポート時にBlendShapeを削減する + /// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する /// public bool ReduceBlendshapeSize = false; #endregion From 3db0de16bbdf513bef7fc5331785a7e09bd04d3a Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 21:47:59 +0900 Subject: [PATCH 08/13] ReduceBlendshapeClip --- .../UniVRM/Editor/Format/VRMEditorExporter.cs | 22 ++++++++++--------- .../UniVRM/Editor/Format/VRMExportSettings.cs | 12 ++++++++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index 5c2474ebdb..31fa03581b 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -42,12 +42,16 @@ static bool IsPrefab(GameObject go) /// /// /// - static BlendShapeAvatar CopyBlendShapeAvatar(BlendShapeAvatar src) + static BlendShapeAvatar CopyBlendShapeAvatar(BlendShapeAvatar src, bool removeUnknown) { var avatar = GameObject.Instantiate(src); avatar.Clips = new List(); foreach (var clip in src.Clips) { + if (removeUnknown && clip.Preset == BlendShapePreset.Unknown) + { + continue; + } avatar.Clips.Add(GameObject.Instantiate(clip)); } return avatar; @@ -128,16 +132,14 @@ static void Export(string path, VRMExportSettings settings, List des } } + // 元のBlendShapeClipに変更を加えないように複製 + var proxy = target.GetComponent(); + var copyBlendShapeAvatar = CopyBlendShapeAvatar(proxy.BlendShapeAvatar, settings.ReduceBlendshapeClip); + proxy.BlendShapeAvatar = copyBlendShapeAvatar; + // BlendShape削減 - if (settings.ReduceBlendshapeSize) + if (settings.ReduceBlendshape) { - // remove unused blendShape - var proxy = target.GetComponent(); - - // 元のBlendShapeClipに変更を加えないように複製 - var copyBlendShapeAvatar = CopyBlendShapeAvatar(proxy.BlendShapeAvatar); - proxy.BlendShapeAvatar = copyBlendShapeAvatar; - foreach (SkinnedMeshRenderer smr in target.GetComponentsInChildren()) { // 未使用のBlendShapeを間引く @@ -148,7 +150,7 @@ static void Export(string path, VRMExportSettings settings, List des // 出力 { var sw = System.Diagnostics.Stopwatch.StartNew(); - var vrm = VRMExporter.Export(target, settings.ReduceBlendshapeSize); + var vrm = VRMExporter.Export(target, settings.ReduceBlendshape); vrm.extensions.VRM.meta.title = settings.Title; vrm.extensions.VRM.meta.version = settings.Version; vrm.extensions.VRM.meta.author = settings.Author; diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 588061205a..207f118666 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -56,12 +56,20 @@ public class VRMExportSettings /// /// エクスポート時に新しいJsonSerializerを使う /// + [Tooltip("Use new JSON serializer")] public bool UseExperimentalExporter = false; /// /// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する /// - public bool ReduceBlendshapeSize = false; + [Tooltip("Remove blendshape that is not used from BlendShapeClip")] + public bool ReduceBlendshape = false; + + /// + /// skip if BlendShapeClip.Preset == Unknown + /// + [Tooltip("Remove blendShapeClip that preset is Unknown")] + public bool ReduceBlendshapeClip = false; #endregion public struct Validation @@ -160,7 +168,7 @@ public IEnumerable Validate() yield return Validation.Error("Require Author. "); } - if (ReduceBlendshapeSize && Source.GetComponent() == null) + if (ReduceBlendshape && Source.GetComponent() == null) { yield return Validation.Error("ReduceBlendshapeSize is need VRMBlendShapeProxy, you need to convert to VRM once."); } From ff90a4957a4038db9e8f553aa48f00e8764d4d70 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 22:03:41 +0900 Subject: [PATCH 09/13] implement UseSparseAccessor and PositionOnly --- .../UniVRM/Editor/Format/VRMEditorExporter.cs | 6 +++++- .../UniVRM/Editor/Format/VRMExportSettings.cs | 11 +++++++++++ Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs | 16 ++++++++++------ .../Scripts/Format/VRMExporterConfiguation.cs | 14 ++++++++++++++ .../Format/VRMExporterConfiguation.cs.meta | 11 +++++++++++ 5 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs create mode 100644 Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index 31fa03581b..6985764b3d 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -150,7 +150,11 @@ static void Export(string path, VRMExportSettings settings, List des // 出力 { var sw = System.Diagnostics.Stopwatch.StartNew(); - var vrm = VRMExporter.Export(target, settings.ReduceBlendshape); + var vrm = VRMExporter.Export(target, new VRMExporterConfiguration + { + UseSparseAccessorForBlendShape = settings.UseSparseAccessorForBlendShape, + ExportOnlyBlendShapePosition = settings.OnlyBlendshapePosition + }); vrm.extensions.VRM.meta.title = settings.Title; vrm.extensions.VRM.meta.version = settings.Version; vrm.extensions.VRM.meta.author = settings.Author; diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 207f118666..7511b80556 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -59,6 +59,17 @@ public class VRMExportSettings [Tooltip("Use new JSON serializer")] public bool UseExperimentalExporter = false; + /// + /// BlendShapeのシリアライズにSparseAccessorを使う + /// + public bool UseSparseAccessorForBlendShape = true; + + /// + /// BlendShapeのPositionのみをエクスポートする + /// + [Tooltip("UniVRM-0.54 or later can load it. Otherwise fail to load")] + public bool OnlyBlendshapePosition = false; + /// /// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する /// diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs b/Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs index 8a03bb0dba..ee588e253e 100644 --- a/Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs +++ b/Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs @@ -19,17 +19,21 @@ public VRMExporter(glTF gltf) : base(gltf) gltf.extensions.VRM = new glTF_VRM_extensions(); } - public new static glTF Export(GameObject go, bool exportOnlyBlendShapePosition = false) + public static glTF Export(GameObject go, bool exportOnlyBlendShapePosition = false) + { + var config = VRMExporterConfiguration.Default; + config.ExportOnlyBlendShapePosition = exportOnlyBlendShapePosition; + return Export(go, config); + } + + public static glTF Export(GameObject go, VRMExporterConfiguration configuration) { var gltf = new glTF(); using (var exporter = new VRMExporter(gltf) { -#if VRM_EXPORTER_USE_SPARSE - // experimental - UseSparseAccessorForBlendShape = true -#endif - ExportOnlyBlendShapePosition = exportOnlyBlendShapePosition + UseSparseAccessorForBlendShape = configuration.UseSparseAccessorForBlendShape, + ExportOnlyBlendShapePosition = configuration.ExportOnlyBlendShapePosition }) { _Export(gltf, exporter, go); diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs b/Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs new file mode 100644 index 0000000000..a416c40d44 --- /dev/null +++ b/Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs @@ -0,0 +1,14 @@ +namespace VRM +{ + public struct VRMExporterConfiguration + { + public bool UseSparseAccessorForBlendShape; + public bool ExportOnlyBlendShapePosition; + + public static VRMExporterConfiguration Default => new VRMExporterConfiguration + { + UseSparseAccessorForBlendShape = true, + ExportOnlyBlendShapePosition = false, + }; + } +} diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs.meta b/Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs.meta new file mode 100644 index 0000000000..8073ec287b --- /dev/null +++ b/Assets/VRM/UniVRM/Scripts/Format/VRMExporterConfiguation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 974aea88b487e5543b1c39e5a528f751 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From eddac4e1fb7352b3858efd3a1065aefa04b9101a Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 2 Jun 2020 22:07:07 +0900 Subject: [PATCH 10/13] change ForceTPose default to false. add tooltip --- Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 7511b80556..a0f420869e 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -46,11 +46,13 @@ public class VRMExportSettings /// /// エクスポート時に強制的にT-Pose化する /// - public bool ForceTPose = true; + [Tooltip("Option")] + public bool ForceTPose = false; /// /// エクスポート時にヒエラルキーの正規化を実施する /// + [Tooltip("Require only first time")] public bool PoseFreeze = true; /// @@ -62,7 +64,8 @@ public class VRMExportSettings /// /// BlendShapeのシリアライズにSparseAccessorを使う /// - public bool UseSparseAccessorForBlendShape = true; + [Tooltip("Use sparse accessor for blendshape. This may reduce vrm size")] + public bool UseSparseAccessor = true; /// /// BlendShapeのPositionのみをエクスポートする @@ -231,7 +234,7 @@ public void InitializeFrom(GameObject go) if (desc == null) { // 初回のVRMエクスポートとみなす - ForceTPose = true; + ForceTPose = false; // option PoseFreeze = true; } else From 41297a8703445f6766e47427109b234b5edfdb5a Mon Sep 17 00:00:00 2001 From: ousttrue Date: Wed, 3 Jun 2020 11:10:18 +0900 Subject: [PATCH 11/13] fix UseSparseAccessor --- Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index 6985764b3d..4ee9a00bfb 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -152,7 +152,7 @@ static void Export(string path, VRMExportSettings settings, List des var sw = System.Diagnostics.Stopwatch.StartNew(); var vrm = VRMExporter.Export(target, new VRMExporterConfiguration { - UseSparseAccessorForBlendShape = settings.UseSparseAccessorForBlendShape, + UseSparseAccessorForBlendShape = settings.UseSparseAccessor, ExportOnlyBlendShapePosition = settings.OnlyBlendshapePosition }); vrm.extensions.VRM.meta.title = settings.Title; From 09eb1c8867ca50bbbc3b2dfc50825bcf494a5436 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Thu, 4 Jun 2020 11:28:24 +0900 Subject: [PATCH 12/13] fix copy --- Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index 4ee9a00bfb..0386f89ebd 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -80,12 +80,13 @@ static void ReplaceMesh(GameObject target, SkinnedMeshRenderer smr, BlendShapeAv // 使われている BlendShape だけをコピーする foreach (var i in usedBlendshapeIndexArray) { - var name = copyMesh.GetBlendShapeName(i); - var vCount = copyMesh.vertexCount; + var name = mesh.GetBlendShapeName(i); + var vCount = mesh.vertexCount; var vertices = new Vector3[vCount]; var normals = new Vector3[vCount]; var tangents = new Vector3[vCount]; mesh.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); + copyMesh.AddBlendShapeFrame(name, 100f, vertices, normals, tangents); } From 361d0d70e99a0237db2cb406c9d920f13fe0620b Mon Sep 17 00:00:00 2001 From: ousttrue Date: Thu, 4 Jun 2020 12:15:20 +0900 Subject: [PATCH 13/13] =?UTF-8?q?prefab=E3=81=AE=E6=99=82=E3=81=A0?= =?UTF-8?q?=E3=81=91copy=E3=81=97=E3=81=A6=E3=81=84=E3=81=9F=E3=81=8C?= =?UTF-8?q?=E3=80=81=E3=82=B7=E3=83=BC=E3=83=B3=E3=81=AE=E6=99=82=E3=82=82?= =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=BC=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E3=81=88=E3=81=9F=E3=80=82=20#423?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 不要になったRecordDisposerを使わない。 * CopyVRMComponents を BoneNormalizer 内部に移動。 --- .../UniVRM/Editor/Format/RecordDisposer.cs | 131 +-------------- .../UniVRM/Editor/Format/VRMEditorExporter.cs | 30 ++-- .../Format/VRMHumanoidNormalizerMenu.cs | 10 +- .../SkinnedMeshUtility/BoneNormalizer.cs | 155 ++++++++++++++++-- 4 files changed, 162 insertions(+), 164 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs index a824ac8bc0..cc3f0557d4 100644 --- a/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs +++ b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs @@ -1,11 +1,13 @@ using System; -using System.Collections.Generic; -using System.Linq; using UnityEditor; -using UnityEngine; namespace VRM { + /// + /// UndoをGroupを開始して、DisposeでUndoする。 + /// using で使うのを想定。 + /// using ブロック内で Undo されるべき操作をする。 + /// public struct RecordDisposer : IDisposable { int _group; @@ -20,128 +22,5 @@ public void Dispose() { Undo.RevertAllDownToGroup(_group); } - - /// - /// VRMを構成するコンポーネントをコピーする。 - /// - /// コピー元 - /// コピー先 - /// コピー元とコピー先の対応関係 - 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.GetComponentsInChildren()) - { - 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.GetComponentsInChildren()) - { - // Copy VRMSpringBone - var dst = dstSecondary.gameObject.AddComponent(); - dst.m_comment = src.m_comment; - dst.m_stiffnessForce = src.m_stiffnessForce; - dst.m_gravityPower = src.m_gravityPower; - dst.m_gravityDir = src.m_gravityDir; - dst.m_dragForce = src.m_dragForce; - if (src.m_center != null) - { - dst.m_center = map[src.m_center]; - } - - dst.RootBones = src.RootBones.Select(x => map[x]).ToList(); - dst.m_hitRadius = src.m_hitRadius; - if (src.ColliderGroups != null) - { - dst.ColliderGroups = src.ColliderGroups - .Select(x => map[x.transform].GetComponent()).ToArray(); - } - } - } - -#pragma warning disable 0618 - { - // meta(obsolete) - var src = go.GetComponent(); - if (src != null) - { - src.CopyTo(root); - } - } -#pragma warning restore 0618 - - { - // 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); - } - } - - { - // 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; - } - } - } - } } } diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index 0386f89ebd..98f7ddac94 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -86,7 +86,7 @@ static void ReplaceMesh(GameObject target, SkinnedMeshRenderer smr, BlendShapeAv var normals = new Vector3[vCount]; var tangents = new Vector3[vCount]; mesh.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); - + copyMesh.AddBlendShapeFrame(name, 100f, vertices, normals, tangents); } @@ -109,28 +109,26 @@ static void ReplaceMesh(GameObject target, SkinnedMeshRenderer smr, BlendShapeAv smr.sharedMesh = copyMesh; } + /// + /// + /// + /// + /// + /// 作業が終わったらDestoryするべき一時オブジェクト static void Export(string path, VRMExportSettings settings, List destroy) { var target = settings.Source; - if (IsPrefab(target)) - { - using (new RecordDisposer(settings.Source.transform.Traverse().ToArray(), "before normalize")) - { - target = GameObject.Instantiate(target); - destroy.Add(target); - } - } + + // 常にコピーする。シーンを変化させない + target = GameObject.Instantiate(target); + destroy.Add(target); // 正規化 if (settings.PoseFreeze) { - using (new RecordDisposer(target.transform.Traverse().ToArray(), "before normalize")) - { - var normalized = BoneNormalizer.Execute(target, settings.ForceTPose, false); - RecordDisposer.CopyVRMComponents(target, normalized.Root, normalized.BoneMap); - target = normalized.Root; - destroy.Add(target); - } + // BoneNormalizer.Execute は Copy を作って正規化する。UNDO無用 + target = BoneNormalizer.Execute(target, settings.ForceTPose, false); + destroy.Add(target); } // 元のBlendShapeClipに変更を加えないように複製 diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs index fbb6d8a320..b02b6ce4ee 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs @@ -48,14 +48,8 @@ private static void ExportFromMenu() { var go = Selection.activeObject as GameObject; - GameObject normalizedRoot = null; - using (new RecordDisposer(go.transform.Traverse().ToArray(), "before normalize")) - { - var normalized = BoneNormalizer.Execute(go, true, false); - RecordDisposer.CopyVRMComponents(go, normalized.Root, normalized.BoneMap); - normalizedRoot = normalized.Root; - } - Selection.activeGameObject = normalizedRoot; + // BoneNormalizer.Execute はコピーを正規化する。UNDO無用 + Selection.activeGameObject = BoneNormalizer.Execute(go, true, false); } } } diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs index 513ed38ddb..2955617744 100644 --- a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs +++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs @@ -181,9 +181,10 @@ Transform[] dstBones var indexMap = srcBones .Select((x, i) => new { i, x }) - .Select(x => { + .Select(x => + { Transform dstBone; - if(boneMap.TryGetValue(x.x, out dstBone)) + if (boneMap.TryGetValue(x.x, out dstBone)) { return dstBones.IndexOf(dstBone); } @@ -191,7 +192,7 @@ Transform[] dstBones { return -1; } - }) + }) .ToArray(); for (int i = 0; i < srcBones.Length; ++i) @@ -322,14 +323,14 @@ static void NormalizeSkinnedMesh(Transform src, Transform dst, Dictionary(); + + var blendShapeValues = new Dictionary(); for (int i = 0; i < srcMesh.blendShapeCount; i++) { var val = srcRenderer.GetBlendShapeWeight(i); if (val > 0) blendShapeValues.Add(i, val); } - + mesh.boneWeights = MapBoneWeight(srcMesh.boneWeights, boneMap, srcRenderer.bones, dstBones); // restore weights. clear when BakeMesh // recalc bindposes @@ -385,7 +386,7 @@ static void NormalizeSkinnedMesh(Transform src, Transform dst, Dictionary対象モデルのルート /// 強制的にT-Pose化するか /// 正規化済みのモデル - public static NormalizedResult Execute(GameObject go, bool forceTPose, bool clearBlendShapeBeforeNormalize) + public static GameObject Execute(GameObject go, bool forceTPose, bool clearBlendShapeBeforeNormalize) { Dictionary boneMap = new Dictionary(); @@ -564,11 +565,137 @@ public static NormalizedResult Execute(GameObject go, bool forceTPose, bool clea NormalizeNoneSkinnedMesh(src, dst); } - return new NormalizedResult + CopyVRMComponents(go, normalized, boneMap); + + // return new NormalizedResult + // { + // Root = normalized, + // BoneMap = boneMap + // }; + return normalized; + } + + /// + /// VRMを構成するコンポーネントをコピーする。 + /// + /// コピー元 + /// コピー先 + /// コピー元とコピー先の対応関係 + 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; + } + } + { - Root = normalized, - BoneMap = boneMap - }; + 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.GetComponentsInChildren()) + { + 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.GetComponentsInChildren()) + { + // Copy VRMSpringBone + var dst = dstSecondary.gameObject.AddComponent(); + dst.m_comment = src.m_comment; + dst.m_stiffnessForce = src.m_stiffnessForce; + dst.m_gravityPower = src.m_gravityPower; + dst.m_gravityDir = src.m_gravityDir; + dst.m_dragForce = src.m_dragForce; + if (src.m_center != null) + { + dst.m_center = map[src.m_center]; + } + + dst.RootBones = src.RootBones.Select(x => map[x]).ToList(); + dst.m_hitRadius = src.m_hitRadius; + if (src.ColliderGroups != null) + { + dst.ColliderGroups = src.ColliderGroups + .Select(x => map[x.transform].GetComponent()).ToArray(); + } + } + } + +#pragma warning disable 0618 + { + // meta(obsolete) + var src = go.GetComponent(); + if (src != null) + { + src.CopyTo(root); + } + } +#pragma warning restore 0618 + + { + // 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); + } + } + + { + // 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; + } + } + } } } }