diff --git a/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs
new file mode 100644
index 000000000..cc3f0557d
--- /dev/null
+++ b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs
@@ -0,0 +1,26 @@
+using System;
+using UnityEditor;
+
+namespace VRM
+{
+ ///
+ /// UndoをGroupを開始して、DisposeでUndoする。
+ /// using で使うのを想定。
+ /// using ブロック内で Undo されるべき操作をする。
+ ///
+ 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);
+ }
+ }
+}
diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs.meta b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta
similarity index 69%
rename from Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs.meta
rename to Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta
index 90b6df455..5cf6bf1e3 100644
--- a/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs.meta
+++ b/Assets/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta
@@ -1,8 +1,7 @@
fileFormatVersion: 2
-guid: f8ed5cb82dd13bf43a1556b24a6d13a0
-timeCreated: 1532063757
-licenseType: Free
+guid: b304ed2aeece5a54191a5a8b69d9d113
MonoImporter:
+ externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs
new file mode 100644
index 000000000..98f7ddac9
--- /dev/null
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UniGLTF;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+ public static class VRMEditorExporter
+ {
+ ///
+ /// Editor向けのエクスポート処理
+ ///
+ /// 出力先
+ /// エクスポート設定
+ 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();
+ }
+
+ ///
+ /// DeepCopy
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// 使用されない 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 = 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);
+ }
+
+ // 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;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 作業が終わったらDestoryするべき一時オブジェクト
+ static void Export(string path, VRMExportSettings settings, List destroy)
+ {
+ var target = settings.Source;
+
+ // 常にコピーする。シーンを変化させない
+ target = GameObject.Instantiate(target);
+ destroy.Add(target);
+
+ // 正規化
+ if (settings.PoseFreeze)
+ {
+ // BoneNormalizer.Execute は Copy を作って正規化する。UNDO無用
+ target = BoneNormalizer.Execute(target, settings.ForceTPose, false);
+ destroy.Add(target);
+ }
+
+ // 元のBlendShapeClipに変更を加えないように複製
+ var proxy = target.GetComponent();
+ var copyBlendShapeAvatar = CopyBlendShapeAvatar(proxy.BlendShapeAvatar, settings.ReduceBlendshapeClip);
+ proxy.BlendShapeAvatar = copyBlendShapeAvatar;
+
+ // BlendShape削減
+ if (settings.ReduceBlendshape)
+ {
+ foreach (SkinnedMeshRenderer smr in target.GetComponentsInChildren())
+ {
+ // 未使用のBlendShapeを間引く
+ ReplaceMesh(target, smr, copyBlendShapeAvatar);
+ }
+ }
+
+ // 出力
+ {
+ var sw = System.Diagnostics.Stopwatch.StartNew();
+ var vrm = VRMExporter.Export(target, new VRMExporterConfiguration
+ {
+ UseSparseAccessorForBlendShape = settings.UseSparseAccessor,
+ ExportOnlyBlendShapePosition = settings.OnlyBlendshapePosition
+ });
+ 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/VRMExportObjectEditor.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs
deleted file mode 100644
index a5c76971f..000000000
--- 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/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
new file mode 100644
index 000000000..a0f420869
--- /dev/null
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
@@ -0,0 +1,266 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UniGLTF;
+using UnityEngine;
+
+
+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化する
+ ///
+ [Tooltip("Option")]
+ public bool ForceTPose = false;
+
+ ///
+ /// エクスポート時にヒエラルキーの正規化を実施する
+ ///
+ [Tooltip("Require only first time")]
+ public bool PoseFreeze = true;
+
+ ///
+ /// エクスポート時に新しいJsonSerializerを使う
+ ///
+ [Tooltip("Use new JSON serializer")]
+ public bool UseExperimentalExporter = false;
+
+ ///
+ /// BlendShapeのシリアライズにSparseAccessorを使う
+ ///
+ [Tooltip("Use sparse accessor for blendshape. This may reduce vrm size")]
+ public bool UseSparseAccessor = true;
+
+ ///
+ /// BlendShapeのPositionのみをエクスポートする
+ ///
+ [Tooltip("UniVRM-0.54 or later can load it. Otherwise fail to load")]
+ public bool OnlyBlendshapePosition = false;
+
+ ///
+ /// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する
+ ///
+ [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
+ {
+ ///
+ /// エクスポート可能か否か。
+ /// true のメッセージは警告
+ /// false のメッセージはエラー
+ ///
+ public readonly bool CanExport;
+ public readonly String Message;
+
+ Validation(bool canExport, string message)
+ {
+ CanExport = canExport;
+ Message = message;
+ }
+
+ public static Validation Error(string msg)
+ {
+ return new Validation(false, msg);
+ }
+
+ public static Validation Warning(string msg)
+ {
+ return new Validation(true, msg);
+ }
+ }
+
+ ///
+ /// ボーン名の重複を確認
+ ///
+ ///
+ 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)
+ {
+ yield return Validation.Error("Require source");
+ yield break;
+ }
+
+ var animator = Source.GetComponent();
+ if (animator == null)
+ {
+ yield return Validation.Error("Require animator. ");
+ }
+ else if (animator.avatar == null)
+ {
+ yield return Validation.Error("Require animator.avatar. ");
+ }
+ else if (!animator.avatar.isValid)
+ {
+ yield return Validation.Error("Animator.avatar is not valid. ");
+ }
+ else if (!animator.avatar.isHuman)
+ {
+ yield return Validation.Error("Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. ");
+ }
+
+ var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
+ if (jaw != null)
+ {
+ yield return Validation.Warning("Jaw bone is included. It may not be what you intended. Please check the humanoid avatar setting screen");
+ }
+
+ if (DuplicateBoneNameExists())
+ {
+ yield return Validation.Error("Find duplicate Bone names. Please check model's bone names. ");
+ }
+
+ if (string.IsNullOrEmpty(Title))
+ {
+ yield return Validation.Error("Require Title. ");
+ }
+ if (string.IsNullOrEmpty(Version))
+ {
+ yield return Validation.Error("Require Version. ");
+ }
+ if (string.IsNullOrEmpty(Author))
+ {
+ yield return Validation.Error("Require Author. ");
+ }
+
+ if (ReduceBlendshape && Source.GetComponent() == null)
+ {
+ yield return Validation.Error("ReduceBlendshapeSize is need VRMBlendShapeProxy, you need to convert to VRM once.");
+ }
+
+ var renderers = Source.GetComponentsInChildren();
+ if (renderers.All(x => !x.gameObject.activeInHierarchy))
+ {
+ yield return Validation.Error("No active mesh");
+ }
+
+ var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct();
+ foreach (var material in materials)
+ {
+ if (material.shader.name == "Standard")
+ {
+ // standard
+ continue;
+ }
+
+ if (MaterialExporter.UseUnlit(material.shader.name))
+ {
+ // unlit
+ continue;
+ }
+
+ if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name))
+ {
+ // VRM supported
+ continue;
+ }
+
+ yield return Validation.Warning(string.Format("unknown material '{0}' is used. this will export as `Standard` fallback", material.shader.name));
+ }
+ }
+
+ ///
+ /// 対象のモデルから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 = false; // option
+ PoseFreeze = true;
+ }
+ else
+ {
+ // すでに正規化済みとみなす
+ ForceTPose = false;
+ PoseFreeze = false;
+ }
+
+ //
+ // Meta
+ //
+ var meta = Source == null ? null : go.GetComponent();
+ if (meta != null && meta.Meta != null)
+ {
+ Title = meta.Meta.Title;
+ Version = string.IsNullOrEmpty(meta.Meta.Version) ? "0.0" : meta.Meta.Version;
+ Author = meta.Meta.Author;
+ ContactInformation = meta.Meta.ContactInformation;
+ Reference = meta.Meta.Reference;
+ }
+ else
+ {
+ Title = go.name;
+ Version = "0.0";
+ }
+ }
+ }
+}
\ No newline at end of file
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/Format/VRMExporterMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs
index 92efb5c29..38bd6da80 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 ee2067f06..5100b682a 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 000000000..90b1faa12
--- /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
+ VRMEditorExporter.Export(path, m_settings);
+ }
+
+ void OnWizardUpdate()
+ {
+ isValid = true;
+ var helpBuilder = new StringBuilder();
+ var errorBuilder = new StringBuilder();
+
+ foreach (var validation in m_settings.Validate())
+ {
+ 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/VRMExportObjectEditor.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta
similarity index 76%
rename from Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs.meta
rename to Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta
index 9a7ef2be7..ee2067f06 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMExportObjectEditor.cs.meta
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: 01708ecf1aa756948be6996d987684a7
-timeCreated: 1532063961
+guid: a8e41aa30fcc76e43ad8588aef8572ea
+timeCreated: 1520491195
licenseType: Free
MonoImporter:
serializedVersion: 2
diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs
index 400dbb2f7..b02b6ce4e 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs
@@ -48,14 +48,8 @@ namespace VRM
{
var go = Selection.activeObject as GameObject;
- GameObject normalizedRoot = null;
- using (new VRMExportSettings.RecordDisposer(go.transform.Traverse().ToArray(), "before normalize"))
- {
- var normalized = BoneNormalizer.Execute(go, true, false);
- VRMExportSettings.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/Editor/UniVRM.Editor.asmdef b/Assets/VRM/UniVRM/Editor/UniVRM.Editor.asmdef
index 35b3e6254..b0fde8cc9 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
diff --git a/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs b/Assets/VRM/UniVRM/Scripts/Format/VRMExportObject.cs
deleted file mode 100644
index 360c47518..000000000
--- 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/VRMExportSettings.cs b/Assets/VRM/UniVRM/Scripts/Format/VRMExportSettings.cs
deleted file mode 100644
index 0416aac32..000000000
--- a/Assets/VRM/UniVRM/Scripts/Format/VRMExportSettings.cs
+++ /dev/null
@@ -1,482 +0,0 @@
-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 Version;
-
- public string Author;
-
- public string ContactInformation;
-
- public string Reference;
-
- public bool ForceTPose = true;
-
- public bool PoseFreeze = true;
-
- public bool UseExperimentalExporter = false;
-
- public bool ReduceBlendshapeSize = false;
-
- public struct Validation
- {
- public readonly bool CanExport;
- public readonly String Message;
-
- Validation(bool canExport, string message)
- {
- CanExport = canExport;
- Message = message;
- }
-
- public static Validation Error(string msg)
- {
- return new Validation(false, msg);
- }
-
- public static Validation Warning(string msg)
- {
- return new Validation(true, msg);
- }
- }
-
- public IEnumerable CanExport()
- {
- if (Source == null)
- {
- yield return Validation.Error("Require source");
- yield break;
- }
-
- var animator = Source.GetComponent();
- if (animator == null)
- {
- yield return Validation.Error("Require animator. ");
- }
- else if (animator.avatar == null)
- {
- yield return Validation.Error("Require animator.avatar. ");
- }
- else if (!animator.avatar.isValid)
- {
- yield return Validation.Error("Animator.avatar is not valid. ");
- }
- else if (!animator.avatar.isHuman)
- {
- yield return Validation.Error("Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. ");
- }
-
- var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
- if (jaw != null)
- {
- yield return Validation.Warning("Jaw bone is included. It may not be what you intended. Please check the humanoid avatar setting screen");
- }
-
- if (DuplicateBoneNameExists())
- {
- yield return Validation.Error("Find duplicate Bone names. Please check model's bone names. ");
- }
-
- if (string.IsNullOrEmpty(Title))
- {
- yield return Validation.Error("Require Title. ");
- }
- if (string.IsNullOrEmpty(Version))
- {
- yield return Validation.Error("Require Version. ");
- }
- if (string.IsNullOrEmpty(Author))
- {
- yield return Validation.Error("Require Author. ");
- }
-
- if (ReduceBlendshapeSize && Source.GetComponent() == null)
- {
- yield return Validation.Error("ReduceBlendshapeSize is need VRMBlendShapeProxy, you need to convert to VRM once.");
- }
-
- var renderers = Source.GetComponentsInChildren();
- if (renderers.All(x => !x.gameObject.activeInHierarchy))
- {
- yield return Validation.Error("No active mesh");
- }
-
- var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct();
- foreach (var material in materials)
- {
- if (material.shader.name == "Standard")
- {
- // standard
- continue;
- }
-
- if (MaterialExporter.UseUnlit(material.shader.name))
- {
- // unlit
- continue;
- }
-
- if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name))
- {
- // VRM supported
- continue;
- }
-
- yield return Validation.Warning(string.Format("unknown material '{0}' is used. this will export as `Standard` fallback", material.shader.name));
- }
- }
-
- 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;
- Version = string.IsNullOrEmpty(meta.Meta.Version) ? "0.0" : meta.Meta.Version;
- Author = meta.Meta.Author;
- ContactInformation = meta.Meta.ContactInformation;
- Reference = meta.Meta.Reference;
- }
- else
- {
- Title = go.name;
- 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/Scripts/Format/VRMExporter.cs b/Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs
index 8a03bb0db..ee588e253 100644
--- a/Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs
+++ b/Assets/VRM/UniVRM/Scripts/Format/VRMExporter.cs
@@ -19,17 +19,21 @@ namespace VRM
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 000000000..a416c40d4
--- /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 000000000..8073ec287
--- /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:
diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs
index 513ed38dd..295561774 100644
--- a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs
+++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs
@@ -181,9 +181,10 @@ namespace VRM
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 @@ namespace VRM
{
return -1;
}
- })
+ })
.ToArray();
for (int i = 0; i < srcBones.Length; ++i)
@@ -322,14 +323,14 @@ namespace VRM
var mesh = srcMesh.Copy(false);
mesh.name = srcMesh.name + ".baked";
srcRenderer.BakeMesh(mesh);
-
- var blendShapeValues = new 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 @@ namespace VRM
srcRenderer.SetBlendShapeWeight(i, value);
Vector3[] vertices = blendShapeMesh.vertices;
-
+
for (int j = 0; j < vertices.Length; ++j)
{
if (originalBlendShapePositions[j] == Vector3.zero)
@@ -404,7 +405,7 @@ namespace VRM
if (originalBlendShapeNormals[j] == Vector3.zero)
{
normals[j] = Vector3.zero;
-
+
}
else
{
@@ -428,7 +429,7 @@ namespace VRM
#endif
var frameCount = srcMesh.GetBlendShapeFrameCount(i);
- for(int f=0; f対象モデルのルート
/// 強制的に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 @@ namespace VRM
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)
+ {
{
- Root = normalized,
- BoneMap = boneMap
- };
+ // 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;
+ }
+ }
+ }
}
}
}