diff --git a/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs
new file mode 100644
index 000000000..a824ac8bc
--- /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 000000000..5cf6bf1e3
--- /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 000000000..0fd5b15a2
--- /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 000000000..f6863a957
--- /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 511650f41..c6ffe529d 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 @@ namespace VRM
}
}
- 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 @@ namespace VRM
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 4e5e90243..90b1faa12 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
@@ -61,7 +61,7 @@ namespace VRM
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
// export
- m_settings.Export(path);
+ VRMEditorExporter.Export(path, m_settings);
}
void OnWizardUpdate()
@@ -70,7 +70,7 @@ namespace VRM
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 400dbb2f7..fbb6d8a32 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs
@@ -49,10 +49,10 @@ namespace VRM
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;