diff --git a/Assets/UniGLTF/Editor/MeshUtility/GameObjectType.cs b/Assets/UniGLTF/Editor/MeshUtility/GameObjectType.cs
index 9f1d09db0..9199406e5 100644
--- a/Assets/UniGLTF/Editor/MeshUtility/GameObjectType.cs
+++ b/Assets/UniGLTF/Editor/MeshUtility/GameObjectType.cs
@@ -13,6 +13,20 @@ namespace UniGLTF.MeshUtility
public static class GameObjectTypeUtility
{
+ public static Object GetPrefab(this GameObject instance)
+ {
+ if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(instance)))
+ {
+ return instance;
+ }
+
+#if UNITY_2018_2_OR_NEWER
+ return PrefabUtility.GetCorrespondingObjectFromSource(instance);
+#else
+ return PrefabUtility.GetPrefabParent(go);
+#endif
+ }
+
public static GameObjectType GetGameObjectType(this GameObject go)
{
if (go == null)
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs
index 11cccb200..ff74aeedf 100644
--- a/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/UnityPath.cs
@@ -343,8 +343,13 @@ namespace UniGLTF
}
public static UnityPath FromAsset(UnityEngine.Object asset)
- {
- return new UnityPath(AssetDatabase.GetAssetPath(asset));
+ {
+ var assetPath = AssetDatabase.GetAssetPath(asset);
+ if (string.IsNullOrEmpty(assetPath))
+ {
+ throw new System.ArgumentNullException();
+ }
+ return new UnityPath(assetPath);
}
public void ImportAsset()
diff --git a/Assets/VRM/Editor/FirstPerson/VRMFirstPersonValidator.cs b/Assets/VRM/Editor/FirstPerson/VRMFirstPersonValidator.cs
index 048159d81..d9ab8fe0d 100644
--- a/Assets/VRM/Editor/FirstPerson/VRMFirstPersonValidator.cs
+++ b/Assets/VRM/Editor/FirstPerson/VRMFirstPersonValidator.cs
@@ -25,11 +25,11 @@ namespace VRM
return false;
}
- if (!r.Renderer.EnableForExport())
- {
- validation = Validation.Error($"{name}.Renderer is not active", ValidationContext.Create(extended));
- return false;
- }
+ // if (!r.Renderer.EnableForExport())
+ // {
+ // validation = Validation.Error($"{name}.Renderer is not active", ValidationContext.Create(extended));
+ // return false;
+ // }
validation = default;
return true;
diff --git a/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs b/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs
new file mode 100644
index 000000000..ee054a596
--- /dev/null
+++ b/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs
@@ -0,0 +1,74 @@
+using System.Collections.Generic;
+using System.Linq;
+using UniGLTF;
+using UniGLTF.MeshUtility;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+ ///
+ /// Meshを統合し、統合後のMeshのBlendShapeの変化をVRMのBlendShapeClipに反映する
+ ///
+ public static class VRMMeshIntegratorUtility
+ {
+ public static void FollowBlendshapeRendererChange(List results, GameObject root, string assetFolder)
+ {
+ var proxy = root.GetComponent();
+ if (proxy == null || proxy.BlendShapeAvatar == null)
+ {
+ return;
+ }
+ var result = results.FirstOrDefault(x => x.SourceSkinnedMeshRenderers.Count > 0);
+ if (result == null)
+ {
+ return;
+ }
+
+ var rendererDict = new Dictionary();
+ foreach (var x in result.SourceSkinnedMeshRenderers)
+ {
+ rendererDict.Add(x.transform.RelativePathFrom(root.transform), x);
+ }
+ var dstPath = result.IntegratedRenderer.transform.RelativePathFrom(root.transform);
+
+ // copy
+ var clips = new List();
+ foreach (var src in proxy.BlendShapeAvatar.Clips)
+ {
+ if (src == null) continue;
+
+ var clip = BlendShapeClip.Instantiate(src);
+ clips.Add(clip);
+ for (var i = 0; i < clip.Values.Length; ++i)
+ {
+ var val = clip.Values[i];
+ if (rendererDict.ContainsKey(val.RelativePath))
+ {
+ var srcRenderer = rendererDict[val.RelativePath];
+ var name = srcRenderer.sharedMesh.GetBlendShapeName(val.Index);
+ var newIndex = result.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name);
+
+ val.RelativePath = dstPath;
+ val.Index = newIndex;
+ }
+
+ clip.Values[i] = val;
+ }
+
+ var assetPath = $"{assetFolder}/{clip.name}.asset";
+ Debug.Log($"write: {assetPath}");
+ AssetDatabase.CreateAsset(clip, assetPath);
+ }
+
+ {
+ // create blendshape avatar & replace
+ var blendShapeAvatar = BlendShapeAvatar.CreateInstance();
+ blendShapeAvatar.Clips.AddRange(clips);
+ var assetPath = $"{assetFolder}/blendshape.asset";
+ AssetDatabase.CreateAsset(blendShapeAvatar, assetPath);
+ proxy.BlendShapeAvatar = blendShapeAvatar;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/VRM/Runtime/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs.meta b/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs.meta
similarity index 83%
rename from Assets/VRM/Runtime/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs.meta
rename to Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs.meta
index 49f2e8d74..84182f709 100644
--- a/Assets/VRM/Runtime/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs.meta
+++ b/Assets/VRM/Editor/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 8eebeb093136b7f429c0e9e7295816b3
+guid: fb47e24fc1463584fa0b6b685d75f25e
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs b/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs
index 86f2fcd86..3797ba473 100644
--- a/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs
+++ b/Assets/VRM/Editor/SkinnedMeshUtility/VrmMeshIntegratorWizard.cs
@@ -221,90 +221,103 @@ namespace VRM
void OnWizardUpdate()
{
- // helpString = "Set target gameobject `in scene`. Prefab not supported.";
}
- ///
- /// Prefab に対する操作として実装されている
- ///
+ /// 2022.05 仕様変更
+ ///
+ /// * scene or prefab どっちでも動作する
+ /// * backup するのではなく 変更した copy を作成する。元は変えない
+ /// * 実行すると mesh, blendshape, blendShape を新規に作成する
+ /// * scene のときは新しいヒエラルキーが出現する
+ /// * prefab のときは新しいヒエラルキーを prefab 保存して、scene の方を削除して終了する
+ ///
void Integrate()
{
- var prefabPath = AssetDatabase.GetAssetPath(m_root);
- Debug.Log(prefabPath);
- var path = EditorUtility.SaveFilePanel("save prefab", Path.GetDirectoryName(prefabPath), m_root.name, "prefab");
- if (string.IsNullOrEmpty(path))
+ String folder = "Assets";
+ var prefab = m_root.GetPrefab();
+ if (prefab != null)
{
- return;
+ folder = AssetDatabase.GetAssetPath(prefab);
+ Debug.Log(folder);
}
- var assetPath = UniGLTF.UnityPath.FromFullpath(path);
- if (!assetPath.IsUnderAssetsFolder)
+ // 新規で作成されるアセットはすべてこのフォルダの中に作る。上書きチェックはしない
+ var assetFolder = EditorUtility.SaveFolderPanel("select asset save folder", Path.GetDirectoryName(folder), "VrmIntegrated");
+ var unityPath = UniGLTF.UnityPath.FromFullpath(assetFolder);
+ if (!unityPath.IsUnderAssetsFolder)
{
- Debug.LogWarning($"{path} is not asset path");
+ EditorUtility.DisplayDialog("asset folder", "Target folder must be in the `Assets` folder", "cancel");
return;
}
+ assetFolder = unityPath.Value;
+ var copy = GameObject.Instantiate(m_root);
+
+ // 統合
var excludes = m_excludes.Where(x => x.Exclude).Select(x => x.Mesh);
+ var results = Integrate(copy, excludes, m_separateByBlendShape);
- // Backup Exists
- VrmPrefabUtility.BackupVrmPrefab(m_root);
+ // 統合前のMeshを非表示にして、統合した結果をヒエラルキーに追加する
+ DeactivateOldRendererAndAddIntegrated(copy, results);
- Undo.RecordObject(m_root, "Mesh Integration");
- var instance = VrmPrefabUtility.InstantiatePrefab(m_root);
- var clips = new List();
- var proxy = instance.GetComponent();
- if (proxy != null && proxy.BlendShapeAvatar != null)
+ // write mesh asset
+ foreach (var result in results)
{
- clips.AddRange(proxy.BlendShapeAvatar.Clips);
- }
- foreach (var clip in clips)
- {
- Undo.RecordObject(clip, "Mesh Integration");
+ var childAssetPath = $"{assetFolder}/{result.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
+ Debug.LogFormat("CreateAsset: {0}", childAssetPath);
+ AssetDatabase.CreateAsset(result.IntegratedRenderer.sharedMesh, childAssetPath);
}
- _Integrate(instance, excludes, assetPath, clips);
+ // 統合した結果を反映した BlendShapeClip を作成して置き換える
+ VRMMeshIntegratorUtility.FollowBlendshapeRendererChange(results, copy, assetFolder);
- // destroy source renderers
- UnityEngine.Object.DestroyImmediate(instance);
- }
-
- void _Integrate(GameObject instance, IEnumerable excludes, UniGLTF.UnityPath assetPath, List clips)
- {
- // Execute
- var results = new List();
- if (m_separateByBlendShape)
+ // reset firstperson
+ var firstperson = copy.GetComponent();
+ if (firstperson != null)
{
- var withoutBlendShape = MeshIntegratorUtility.Integrate(instance, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithoutBlendShape, excludes: excludes);
- if (withoutBlendShape.IntegratedRenderer != null)
+ firstperson.Reset();
+ }
+
+ if (prefab != null)
+ {
+ // prefab
+ var prefabPath = $"{assetFolder}/VrmIntegrated.prefab";
+ Debug.Log(prefabPath);
+ PrefabUtility.SaveAsPrefabAsset(copy, prefabPath, out bool success);
+ if (!success)
{
- results.Add(withoutBlendShape);
+ throw new System.Exception($"PrefabUtility.SaveAsPrefabAsset: {prefabPath}");
}
- var onlyBlendShape = MeshIntegratorUtility.Integrate(instance, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithBlendShape, excludes: excludes);
- if (onlyBlendShape.IntegratedRenderer != null)
- {
- results.Add(onlyBlendShape);
- VRMMeshIntegratorUtility.FollowBlendshapeRendererChange(clips, onlyBlendShape, instance);
- }
+ // destroy scene
+ UnityEngine.Object.DestroyImmediate(copy);
}
else
{
- var integrated = MeshIntegratorUtility.Integrate(instance, onlyBlendShapeRenderers: MeshEnumerateOption.All, excludes: excludes);
- if (integrated.IntegratedRenderer != null)
- {
- results.Add(integrated);
- }
+ // do nothing. keep scene
}
-
- DeactivateOldRendererAndAddIntegrated(instance, results, assetPath,
- UniGLTF.UnityPath.FromUnityPath(AssetDatabase.GetAssetPath(m_root)).Equals(assetPath));
}
- static void DeactivateOldRendererAndAddIntegrated(GameObject instance, List results, UniGLTF.UnityPath assetPath,
- bool applyToPrefab)
+ static List Integrate(GameObject root, IEnumerable excludes, bool separateByBlendShape)
{
- // TODO: add integrated
+ var results = new List();
+ if (separateByBlendShape)
+ {
+ results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithoutBlendShape, excludes: excludes));
+ results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithBlendShape, excludes: excludes));
+ }
+ else
+ {
+ results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.All, excludes: excludes));
+ }
+ return results;
+ }
+ ///
+ /// 古いMeshを disable にし、新しい統合済み mesh を追加する
+ ///
+ static void DeactivateOldRendererAndAddIntegrated(GameObject root, List results)
+ {
// disable source renderer
foreach (var result in results)
{
@@ -321,31 +334,12 @@ namespace VRM
}
}
+ // Add integrated
foreach (var result in results)
{
- if (result.IntegratedRenderer == null) continue;
-
- // Add integrated
- result.IntegratedRenderer.transform.SetParent(instance.transform, false);
-
- // save as asset mesh
- var childAssetPath = assetPath.Parent.Child($"{result.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}");
- Debug.LogFormat("CreateAsset: {0}", childAssetPath);
- childAssetPath.CreateAsset(result.IntegratedRenderer.sharedMesh);
- Undo.RegisterCreatedObjectUndo(result.IntegratedRenderer.gameObject, "Integrate Renderers");
- }
-
- // Apply to Prefab
- if (applyToPrefab)
- {
- VrmPrefabUtility.ApplyChangesToPrefab(instance);
- }
- else
- {
- PrefabUtility.SaveAsPrefabAsset(instance, assetPath.Value, out bool success);
- if (!success)
+ if (result.IntegratedRenderer != null)
{
- throw new System.Exception($"PrefabUtility.SaveAsPrefabAsset: {assetPath}");
+ result.IntegratedRenderer.transform.SetParent(root.transform, false);
}
}
}
diff --git a/Assets/VRM/Editor/SkinnedMeshUtility/VrmPrefabUtility.cs b/Assets/VRM/Editor/SkinnedMeshUtility/VrmPrefabUtility.cs
index 574c7121c..0f203752b 100644
--- a/Assets/VRM/Editor/SkinnedMeshUtility/VrmPrefabUtility.cs
+++ b/Assets/VRM/Editor/SkinnedMeshUtility/VrmPrefabUtility.cs
@@ -2,6 +2,7 @@ using System.Linq;
using UniGLTF;
using UnityEditor;
using UnityEngine;
+using UniGLTF.MeshUtility;
namespace VRM
{
@@ -31,7 +32,7 @@ namespace VRM
public static void ApplyChangesToPrefab(GameObject instance)
{
- var prefab = GetPrefab(instance);
+ var prefab = instance.GetPrefab();
if (prefab == null)
{
return;
@@ -46,15 +47,6 @@ namespace VRM
PrefabUtility.SaveAsPrefabAssetAndConnect(instance, path, InteractionMode.AutomatedAction);
}
- static Object GetPrefab(GameObject instance)
- {
-#if UNITY_2018_2_OR_NEWER
- return PrefabUtility.GetCorrespondingObjectFromSource(instance);
-#else
- return PrefabUtility.GetPrefabParent(go);
-#endif
- }
-
///
/// VRM prefab を ${prefab_dir}/MeshIntegratorBackup/ に複製する。
///
@@ -64,18 +56,21 @@ namespace VRM
///
/// が複製される。
///
- ///
- public static void BackupVrmPrefab(GameObject rootPrefab)
+ ///
+ public static void BackupVrmPrefab(GameObject go)
{
- var proxy = rootPrefab.GetComponent();
+ var prefab = go.GetGameObjectType() == GameObjectType.AssetPrefab ? go : PrefabUtility.GetCorrespondingObjectFromSource(go);
+ var prefabPath = UnityPath.FromAsset(prefab);
+
+ var proxy = go.GetComponent();
var srcAvatar = proxy.BlendShapeAvatar;
- var dstAvatar = (BlendShapeAvatar)BackupAsset(srcAvatar, rootPrefab);
+ var dstAvatar = (BlendShapeAvatar)BackupAsset(srcAvatar, prefabPath);
- var clipMapper = srcAvatar.Clips.ToDictionary(x => x, x => (BlendShapeClip)BackupAsset(x, rootPrefab));
+ var clipMapper = srcAvatar.Clips.ToDictionary(x => x, x => (BlendShapeClip)BackupAsset(x, prefabPath));
dstAvatar.Clips = clipMapper.Values.ToList();
- var dstPrefab = BackupAsset(rootPrefab, rootPrefab);
+ var dstPrefab = BackupAsset(prefab, prefabPath);
var dstInstance = InstantiatePrefab(dstPrefab);
dstInstance.GetComponent().BlendShapeAvatar = dstAvatar;
ApplyChangesToPrefab(dstInstance);
@@ -89,12 +84,12 @@ namespace VRM
///
///
///
- private static T BackupAsset(T asset, GameObject rootPrefab) where T : UnityEngine.Object
+ private static T BackupAsset(T asset, UnityPath prefabPath) where T : UnityEngine.Object
{
var srcAssetPath = UnityPath.FromAsset(asset);
var assetName = srcAssetPath.FileName;
- var backupPath = UnityPath.FromAsset(rootPrefab).Parent.Child(BACKUP_DIR);
+ var backupPath = prefabPath.Parent.Child(BACKUP_DIR);
backupPath.EnsureFolder();
var dstAssetPath = backupPath.Child(assetName);
diff --git a/Assets/VRM/Runtime/FirstPerson/VRMFirstPerson.cs b/Assets/VRM/Runtime/FirstPerson/VRMFirstPerson.cs
index d326b9959..1b600df6b 100644
--- a/Assets/VRM/Runtime/FirstPerson/VRMFirstPerson.cs
+++ b/Assets/VRM/Runtime/FirstPerson/VRMFirstPerson.cs
@@ -91,6 +91,12 @@ namespace VRM
var rendererComponents = transform.GetComponentsInChildren();
foreach (var renderer in rendererComponents)
{
+ // renderer が !enabled/!activeSelf なのがロード中なのか否か区別がつかないような気がするので
+ // チェックしない。
+ // if(!renderer.enabled)
+ // {
+ // continue;
+ // }
var flags = new RendererFirstPersonFlags
{
Renderer = renderer,
diff --git a/Assets/VRM/Runtime/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs b/Assets/VRM/Runtime/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs
deleted file mode 100644
index 066b55fbd..000000000
--- a/Assets/VRM/Runtime/SkinnedMeshUtility/VRMMeshIntegratorUtility.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using UniGLTF;
-using UniGLTF.MeshUtility;
-using UnityEngine;
-
-namespace VRM
-{
- ///
- /// Meshを統合し、統合後のMeshのBlendShapeの変化をVRMのBlendShapeClipに反映する
- ///
- public static class VRMMeshIntegratorUtility
- {
- public static void FollowBlendshapeRendererChange(List clips, MeshIntegrationResult result, GameObject root)
- {
- if (clips == null || result == null || result.IntegratedRenderer == null || root == null) return;
-
- var rendererDict = result.SourceSkinnedMeshRenderers
- .ToDictionary(x => x.transform.RelativePathFrom(root.transform), x => x);
-
- var dstPath = result.IntegratedRenderer.transform.RelativePathFrom(root.transform);
-
- foreach (var clip in clips)
- {
- if (clip == null) continue;
-
- for (var i = 0; i < clip.Values.Length; ++i)
- {
- var val = clip.Values[i];
- if (rendererDict.ContainsKey(val.RelativePath))
- {
- var srcRenderer = rendererDict[val.RelativePath];
- var name = srcRenderer.sharedMesh.GetBlendShapeName(val.Index);
- var newIndex = result.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name);
-
- val.RelativePath = dstPath;
- val.Index = newIndex;
- }
-
- clip.Values[i] = val;
- }
- }
- }
- }
-}
\ No newline at end of file