diff --git a/Assets/VRM/UniGLTF/Scripts/IO/UnityPath.cs b/Assets/VRM/UniGLTF/Scripts/IO/UnityPath.cs
index 0a19f33af..bc04c4ba8 100644
--- a/Assets/VRM/UniGLTF/Scripts/IO/UnityPath.cs
+++ b/Assets/VRM/UniGLTF/Scripts/IO/UnityPath.cs
@@ -57,6 +57,11 @@ namespace UniGLTF
}
}
+ public string FileName
+ {
+ get { return Path.GetFileName(Value); }
+ }
+
public string FileNameWithoutExtension
{
get { return Path.GetFileNameWithoutExtension(Value); }
diff --git a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/BoneMeshEraserWizard.cs b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/BoneMeshEraserWizard.cs
index 09e068124..081118cbc 100644
--- a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/BoneMeshEraserWizard.cs
+++ b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/BoneMeshEraserWizard.cs
@@ -168,11 +168,7 @@ namespace VRM
// save mesh to Assets
var assetPath = string.Format("{0}{1}", go.name, ASSET_SUFFIX);
-#if UNITY_2018_2_OR_NEWER
- var prefab = PrefabUtility.GetCorrespondingObjectFromSource(go);
-#else
- var prefab = PrefabUtility.GetPrefabParent(go);
-#endif
+ var prefab = SkinnedMeshUtility.GetPrefab(go);
if (prefab != null)
{
var prefabPath = AssetDatabase.GetAssetPath(prefab);
diff --git a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegrator.cs b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegrator.cs
deleted file mode 100644
index 81893a8fb..000000000
--- a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegrator.cs
+++ /dev/null
@@ -1,461 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using UnityEditor;
-using UnityEngine;
-
-
-namespace VRM
-{
- ///
- /// 複数のメッシュをまとめる
- ///
- [DisallowMultipleComponent]
- public static class MeshIntegrator
- {
- const string MENU_KEY = SkinnedMeshUtility.MENU_KEY + "MeshIntegrator";
- const string ASSET_SUFFIX = ".mesh.asset";
- const string ASSET_WITH_BLENDSHAPE_SUFFIX = ".blendshape.asset";
-
- [MenuItem(MENU_KEY, true, SkinnedMeshUtility.MENU_PRIORITY)]
- private static bool ExportValidate()
- {
- return Selection.activeObject != null && Selection.activeObject is GameObject;
- }
-
- [MenuItem(MENU_KEY, false, SkinnedMeshUtility.MENU_PRIORITY)]
- private static void ExportFromMenu()
- {
- var go = Selection.activeObject as GameObject;
-
- Integrate(go);
- }
-
- public static SkinnedMeshRenderer Integrate(GameObject go)
- {
- var without_blendshape = _Integrate(go, false);
- if (without_blendshape == null)
- {
- return null;
-
- }
-
- // save mesh to Assets
- var assetPath = string.Format("{0}{1}", go.name, ASSET_SUFFIX);
-#if UNITY_2018_2_OR_NEWER
- var prefab = PrefabUtility.GetCorrespondingObjectFromSource(go);
-#else
- var prefab = PrefabUtility.GetPrefabParent(go);
-#endif
- if (prefab != null)
- {
- var prefabPath = AssetDatabase.GetAssetPath(prefab);
- assetPath = string.Format("{0}/{1}_{2}{3}",
- Path.GetDirectoryName(prefabPath),
- Path.GetFileNameWithoutExtension(prefabPath),
- go.name,
- ASSET_SUFFIX
- );
- }
- else
- {
- assetPath = string.Format("Assets/{0}{1}", go.name, ASSET_SUFFIX);
- }
-
- Debug.LogFormat("CreateAsset: {0}", assetPath);
- AssetDatabase.CreateAsset(without_blendshape.sharedMesh, assetPath);
- return without_blendshape;
- }
-
- struct SubMesh
- {
- public List Indices;
- public Material Material;
- }
-
- class BlendShape
- {
- public int VertexOffset;
- public string Name;
- public float FrameWeight;
- public Vector3[] Positions;
- public Vector3[] Normals;
- public Vector3[] Tangents;
- }
-
- class Integrator
- {
-// public List Renderers { get; private set; }
- public List Positions { get; private set; }
- public List Normals { get; private set; }
- public List UV { get; private set; }
- public List Tangents { get; private set; }
- public List BoneWeights { get; private set; }
-
- public List SubMeshes
- {
- get;
- private set;
- }
-
- public List BindPoses { get; private set; }
- public List Bones { get; private set; }
-
- public List BlendShapes { get; private set; }
- public void AddBlendShapesToMesh(Mesh mesh)
- {
- Dictionary map = new Dictionary();
-
- foreach (var x in BlendShapes)
- {
- BlendShape bs = null;
- if (!map.TryGetValue(x.Name, out bs))
- {
- bs = new BlendShape();
- bs.Positions = Positions.ToArray();
- bs.Normals = Normals.ToArray();
- bs.Tangents = Tangents.Select(y => (Vector3)y).ToArray();
- bs.Name = x.Name;
- bs.FrameWeight = x.FrameWeight;
- map.Add(x.Name, bs);
- }
-
- var j = x.VertexOffset;
- for (int i = 0; i < x.Positions.Length; ++i, ++j)
- {
- bs.Positions[j] = x.Positions[i];
- bs.Normals[j] = x.Normals[i];
- bs.Tangents[j] = x.Tangents[i];
- }
- }
-
- foreach (var kv in map)
- {
- //Debug.LogFormat("AddBlendShapeFrame: {0}", kv.Key);
- mesh.AddBlendShapeFrame(kv.Key, kv.Value.FrameWeight,
- kv.Value.Positions, kv.Value.Normals, kv.Value.Tangents);
- }
- }
-
- public Integrator()
- {
-// Renderers = new List();
-
- Positions = new List();
- Normals = new List();
- UV = new List();
- Tangents = new List();
- BoneWeights = new List();
-
- SubMeshes = new List();
-
- BindPoses = new List();
- Bones = new List();
-
- BlendShapes = new List();
- }
-
- static BoneWeight AddBoneIndexOffset(BoneWeight bw, int boneIndexOffset)
- {
- if (bw.weight0 > 0) bw.boneIndex0 += boneIndexOffset;
- if (bw.weight1 > 0) bw.boneIndex1 += boneIndexOffset;
- if (bw.weight2 > 0) bw.boneIndex2 += boneIndexOffset;
- if (bw.weight3 > 0) bw.boneIndex3 += boneIndexOffset;
- return bw;
- }
-
- public void Push(MeshRenderer renderer)
- {
- var meshFilter = renderer.GetComponent();
- if (meshFilter == null)
- {
- Debug.LogWarningFormat("{0} has no mesh filter", renderer.name);
- return;
- }
- var mesh = meshFilter.sharedMesh;
- if (mesh == null)
- {
- Debug.LogWarningFormat("{0} has no mesh", renderer.name);
- return;
- }
-
- var indexOffset = Positions.Count;
- var boneIndexOffset = Bones.Count;
-
- Positions.AddRange(mesh.vertices
- .Select(x => renderer.transform.TransformPoint(x))
- );
- Normals.AddRange(mesh.normals
- .Select(x => renderer.transform.TransformVector(x))
- );
- UV.AddRange(mesh.uv);
- Tangents.AddRange(mesh.tangents
- .Select(t =>
- {
- var v = renderer.transform.TransformVector(t.x, t.y, t.z);
- return new Vector4(v.x, v.y, v.z, t.w);
- })
- );
-
- var self = renderer.transform;
- var bone = self.parent;
- if (bone == null)
- {
- Debug.LogWarningFormat("{0} is root gameobject.", self.name);
- return;
- }
- var bindpose = bone.worldToLocalMatrix;
-
- BoneWeights.AddRange(Enumerable.Range(0, mesh.vertices.Length)
- .Select(x => new BoneWeight()
- {
- boneIndex0 = Bones.Count,
- weight0 = 1,
- })
- );
-
- BindPoses.Add(bindpose);
- Bones.Add(bone);
-
- for (int i = 0; i < mesh.subMeshCount; ++i)
- {
- var indices = mesh.GetIndices(i).Select(x => x + indexOffset);
- var mat = renderer.sharedMaterials[i];
- var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat));
- if (sameMaterialSubMeshIndex >= 0)
- {
- SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices);
- }
- else
- {
- SubMeshes.Add(new SubMesh
- {
- Indices = indices.ToList(),
- Material = mat,
- });
- }
- }
- }
-
- public void Push(SkinnedMeshRenderer renderer)
- {
- var mesh = renderer.sharedMesh;
- if (mesh == null)
- {
- Debug.LogWarningFormat("{0} has no mesh", renderer.name);
- return;
- }
-
-// Renderers.Add(renderer);
-
- var indexOffset = Positions.Count;
- var boneIndexOffset = Bones.Count;
-
- Positions.AddRange(mesh.vertices);
- Normals.AddRange(mesh.normals);
- UV.AddRange(mesh.uv);
- Tangents.AddRange(mesh.tangents);
-
- if (mesh.vertexCount == mesh.boneWeights.Length)
- {
- BoneWeights.AddRange(mesh.boneWeights.Select(x => AddBoneIndexOffset(x, boneIndexOffset)).ToArray());
- }
- else
- {
- BoneWeights.AddRange(Enumerable.Range(0, mesh.vertexCount).Select(x => new BoneWeight()).ToArray());
- }
-
- BindPoses.AddRange(mesh.bindposes);
- Bones.AddRange(renderer.bones);
-
- for (int i = 0; i < mesh.subMeshCount; ++i)
- {
- var indices = mesh.GetIndices(i).Select(x => x + indexOffset);
- var mat = renderer.sharedMaterials[i];
- var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat));
- if (sameMaterialSubMeshIndex >= 0)
- {
- SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices);
- }
- else
- {
- SubMeshes.Add(new SubMesh
- {
- Indices = indices.ToList(),
- Material = mat,
- });
- }
- }
-
- for (int i = 0; i < mesh.blendShapeCount; ++i)
- {
- var positions = (Vector3[])mesh.vertices.Clone();
- var normals = (Vector3[])mesh.normals.Clone();
- var tangents = mesh.tangents.Select(x => (Vector3)x).ToArray();
-
- mesh.GetBlendShapeFrameVertices(i, 0, positions, normals, tangents);
- BlendShapes.Add(new BlendShape
- {
- VertexOffset = indexOffset,
- FrameWeight = mesh.GetBlendShapeFrameWeight(i, 0),
- Name = mesh.GetBlendShapeName(i),
- Positions = positions,
- Normals = normals,
- Tangents = tangents,
- });
- }
- }
- }
-
- static IEnumerable Traverse(Transform parent)
- {
- if (parent.gameObject.activeSelf)
- {
- yield return parent;
-
- foreach (Transform child in parent)
- {
- foreach (var x in Traverse(child))
- {
- yield return x;
- }
- }
- }
- }
-
- static public IEnumerable EnumerateRenderer(Transform root, bool hasBlendShape)
- {
- foreach (var x in Traverse(root))
- {
- var renderer = x.GetComponent();
- if (renderer != null)
- {
- if (renderer.sharedMesh != null)
- {
- if (renderer.gameObject.activeSelf)
- {
- if (renderer.sharedMesh.blendShapeCount > 0 == hasBlendShape)
- {
- yield return renderer;
- }
- }
- }
- }
- }
- }
-
- static IEnumerable EnumerateMeshRenderer(Transform root)
- {
- foreach (var x in Traverse(root))
- {
- var renderer = x.GetComponent();
- if (renderer != null)
- {
- var filter = x.GetComponent();
- if (filter != null && filter.sharedMesh != null && renderer.gameObject.activeSelf)
- {
- yield return renderer;
- }
- }
- }
- }
-
- static IEnumerable Ancestors(Transform self)
- {
- yield return self;
-
- if (self.parent != null)
- {
- foreach (var x in Ancestors(self.parent))
- {
- yield return x;
- }
- }
- }
-
- static SkinnedMeshRenderer _Integrate(GameObject go, bool hasBlendShape)
- {
- var meshNode = new GameObject();
- if (hasBlendShape)
- {
- meshNode.name = "MeshIntegrator(BlendShape)";
- }
- else
- {
- meshNode.name = "MeshIntegrator";
- }
- meshNode.transform.SetParent(go.transform, false);
-
- var renderers = EnumerateRenderer(go.transform, hasBlendShape).ToArray();
-
- // Root objectを選出する
- var root = renderers.Select(x => x.rootBone != null ? x.rootBone : x.transform)
- .Select(x => Ancestors(x).Reverse().ToArray())
- .Aggregate((a, b) =>
- {
- int i = 0;
- for(; i ushort.MaxValue)
- {
-#if UNITY_2017_3_OR_NEWER
- Debug.LogFormat("exceed 65535 vertices: {0}", integrator.Positions.Count);
- mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
-#else
- throw new NotImplementedException(String.Format("exceed 65535 vertices: {0}", integrator.Positions.Count.ToString()));
-#endif
- }
-
- mesh.vertices = integrator.Positions.ToArray();
- mesh.normals = integrator.Normals.ToArray();
- mesh.uv = integrator.UV.ToArray();
- mesh.tangents = integrator.Tangents.ToArray();
- mesh.boneWeights = integrator.BoneWeights.ToArray();
- mesh.subMeshCount = integrator.SubMeshes.Count;
- for (var i = 0; i < integrator.SubMeshes.Count; ++i)
- {
- mesh.SetIndices(integrator.SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i);
- }
- mesh.bindposes = integrator.BindPoses.ToArray();
-
- if (hasBlendShape)
- {
- integrator.AddBlendShapesToMesh(mesh);
- }
-
- var integrated = meshNode.AddComponent();
- integrated.sharedMesh = mesh;
- integrated.sharedMaterials = integrator.SubMeshes.Select(x => x.Material).ToArray();
- integrated.bones = integrator.Bones.ToArray();
-
- return integrated;
- }
- }
-}
diff --git a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs
new file mode 100644
index 000000000..23518027b
--- /dev/null
+++ b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UniGLTF;
+using UnityEditor;
+using UnityEngine;
+using Object = UnityEngine.Object;
+
+
+namespace VRM
+{
+ ///
+ /// 複数のメッシュをまとめる
+ ///
+ [DisallowMultipleComponent]
+ public static class MeshIntegratorEditor
+ {
+ const string MENU_KEY = "Assets/UnityEditorScripts/MeshIntegrator";
+ const string ASSET_SUFFIX = ".mesh.asset";
+
+ [MenuItem(MENU_KEY, true)]
+ private static bool ExportValidate()
+ {
+ return Selection.activeObject != null &&
+ Selection.activeObject is GameObject &&
+ SkinnedMeshUtility.IsPrefab(Selection.activeObject);
+ }
+
+ [MenuItem(MENU_KEY, false)]
+ private static void ExportFromMenu()
+ {
+ var go = Selection.activeObject as GameObject;
+
+ Integrate(go);
+ }
+
+ [MenuItem("Assets/fooa", false)]
+ private static void Foo()
+ {
+ var go = Selection.activeObject as GameObject;
+
+ Debug.Log(SkinnedMeshUtility.IsPrefab(go));
+ }
+
+
+ public static List Integrate(GameObject prefab)
+ {
+ Undo.RecordObject(prefab, "Mesh Integration");
+ var instance = SkinnedMeshUtility.InstantiatePrefab(prefab);
+
+
+ var clips = new List();
+ var proxy = instance.GetComponent();
+ if (proxy != null && proxy.BlendShapeAvatar != null)
+ {
+ clips = proxy.BlendShapeAvatar.Clips;
+ }
+ foreach (var clip in clips)
+ {
+ Undo.RecordObject(clip, "Mesh Integration");
+ }
+
+ // Backup Exists
+ BackupVrmPrefab(prefab);
+
+ // Execute
+ var results = MeshIntegratorUtility.Integrate(instance, clips);
+
+ foreach (var res in results)
+ {
+ if (res.IntegratedRenderer == null) continue;
+
+ SaveMeshAsset(res.IntegratedRenderer.sharedMesh, instance, res.IntegratedRenderer.gameObject.name);
+ Undo.RegisterCreatedObjectUndo(res.IntegratedRenderer.gameObject, "Integrate Renderers");
+ }
+
+ // destroy source renderers
+ foreach (var res in results)
+ {
+ foreach (var renderer in res.SourceSkinnedMeshRenderers)
+ {
+ Undo.RecordObject(renderer.gameObject, "Deactivate old renderer");
+ renderer.gameObject.SetActive(false);
+ }
+
+ foreach (var renderer in res.SourceMeshRenderers)
+ {
+ Undo.RecordObject(renderer.gameObject, "Deactivate old renderer");
+ renderer.gameObject.SetActive(false);
+ }
+ }
+
+ // Apply to Prefab
+ SkinnedMeshUtility.ApplyChangesToPrefab(instance);
+ Object.DestroyImmediate(instance);
+
+ return results;
+ }
+
+ private static void BackupVrmPrefab(GameObject rootPrefab)
+ {
+ var proxy = rootPrefab.GetComponent();
+
+ var srcAvatar = proxy.BlendShapeAvatar;
+ var dstAvatar = (BlendShapeAvatar) BackupAsset(srcAvatar, rootPrefab);
+
+ var clipMapper = srcAvatar.Clips.ToDictionary(x => x, x => (BlendShapeClip) BackupAsset(x, rootPrefab));
+ dstAvatar.Clips = clipMapper.Values.ToList();
+
+ var dstPrefab = BackupAsset(rootPrefab, rootPrefab);
+ var dstInstance = SkinnedMeshUtility.InstantiatePrefab(dstPrefab);
+ dstInstance.GetComponent().BlendShapeAvatar = dstAvatar;
+ SkinnedMeshUtility.ApplyChangesToPrefab(dstInstance);
+ Object.DestroyImmediate(dstInstance);
+ }
+
+ private static T BackupAsset(T asset, GameObject rootPrefab) where T : UnityEngine.Object
+ {
+ var srcAssetPath = UnityPath.FromAsset(asset);
+ var assetName = srcAssetPath.FileName;
+
+ var backupDir = "MeshIntegratorBackup";
+ var backupPath = UnityPath.FromAsset(rootPrefab).Parent.Child(backupDir);
+ backupPath.EnsureFolder();
+ var dstAssetPath = backupPath.Child(assetName);
+
+ AssetDatabase.CopyAsset(srcAssetPath.Value, dstAssetPath.Value);
+ return dstAssetPath.LoadAsset();
+ }
+
+ private static string GetRootPrefabPath(GameObject go)
+ {
+ var prefab = SkinnedMeshUtility.IsPrefab(go) ? go : SkinnedMeshUtility.GetPrefab(go);
+ var assetPath = "";
+ if (prefab != null)
+ {
+ var prefabPath = AssetDatabase.GetAssetPath(prefab);
+ assetPath = string.Format("{0}/", Path.GetDirectoryName(prefabPath));
+ }
+ else
+ {
+ assetPath = string.Format("Assets/");
+ }
+ assetPath = assetPath.Replace(@"\", @"/");
+ return assetPath;
+ }
+
+ private static void SaveMeshAsset(Mesh mesh, GameObject go, string name)
+ {
+ var assetPath = Path.Combine(GetRootPrefabPath(go), string.Format("{0}{1}",
+ name,
+ ASSET_SUFFIX
+ ));
+
+ Debug.LogFormat("CreateAsset: {0}", assetPath);
+ AssetDatabase.CreateAsset(mesh, assetPath);
+ }
+
+ }
+}
diff --git a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegrator.cs.meta b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs.meta
similarity index 100%
rename from Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegrator.cs.meta
rename to Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs.meta
diff --git a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs
index 9e45543b0..cd93e3237 100644
--- a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs
+++ b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs
@@ -10,6 +10,8 @@ namespace VRM
{
public class MeshIntegratorWizard : ScriptableWizard
{
+ const string MENU_KEY = "Assets/UnityEditorScripts/MeshIntegratorWizard";
+
[SerializeField]
GameObject m_root;
@@ -56,9 +58,9 @@ namespace VRM
MaterialList[] m_duplicateMaterials;
[Header("Result")]
- public Mesh integrated;
+ public MeshIntegratorUtility.MeshIntegrationResult[] integrationResults;
- [MenuItem(SkinnedMeshUtility.MENU_KEY + "MeshIntegrator Wizard", priority = SkinnedMeshUtility.MENU_PRIORITY)]
+ [MenuItem(MENU_KEY)]
static void CreateWizard()
{
ScriptableWizard.DisplayWizard("MeshIntegrator", "Integrate and close window", "Integrate");
@@ -122,7 +124,7 @@ namespace VRM
return;
}
- m_uniqueMaterials = MeshIntegrator.EnumerateRenderer(m_root.transform, false)
+ m_uniqueMaterials = MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, false)
.SelectMany(x => x.sharedMaterials)
.Distinct()
.ToArray();
@@ -148,13 +150,7 @@ namespace VRM
return;
}
- var renderer = MeshIntegrator.Integrate(m_root);
- if (renderer == null)
- {
- return;
- }
-
- integrated = renderer.sharedMesh;
+ integrationResults = MeshIntegratorEditor.Integrate(m_root).ToArray();
}
void OnWizardCreate()
diff --git a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs
index baf387791..c8f9c143c 100644
--- a/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs
+++ b/Assets/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs
@@ -1,8 +1,42 @@
+using System;
+using UnityEditor;
+using UnityEngine;
+using Object = UnityEngine.Object;
+
namespace VRM
{
public static class SkinnedMeshUtility
{
public const string MENU_KEY = "GameObject/UnityEditorScripts/";
public const int MENU_PRIORITY = 11;
+
+ public static Object GetPrefab(GameObject instance)
+ {
+#if UNITY_2018_2_OR_NEWER
+ return PrefabUtility.GetCorrespondingObjectFromSource(instance);
+#else
+ return PrefabUtility.GetPrefabParent(go);
+#endif
+ }
+
+ public static bool IsPrefab(Object instance)
+ {
+ return instance != null && PrefabUtility.GetPrefabType(instance) == PrefabType.Prefab;
+ }
+
+ public static void ApplyChangesToPrefab(GameObject instance)
+ {
+ var prefab = GetPrefab(instance);
+ if (prefab == null) return;
+
+ PrefabUtility.ReplacePrefab(instance, prefab, ReplacePrefabOptions.ConnectToPrefab);
+ }
+
+ public static GameObject InstantiatePrefab(GameObject prefab)
+ {
+ if (!IsPrefab(prefab)) return null;
+
+ return (GameObject) PrefabUtility.InstantiatePrefab(prefab);
+ }
}
}
\ No newline at end of file
diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegrator.cs b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegrator.cs
new file mode 100644
index 000000000..8c0dd6f72
--- /dev/null
+++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegrator.cs
@@ -0,0 +1,253 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace VRM
+{
+ public class MeshIntegrator
+ {
+ public struct SubMesh
+ {
+ public List Indices;
+ public Material Material;
+ }
+
+ public class BlendShape
+ {
+ public int VertexOffset;
+ public string Name;
+ public float FrameWeight;
+ public Vector3[] Positions;
+ public Vector3[] Normals;
+ public Vector3[] Tangents;
+ }
+
+// public List Renderers { get; private set; }
+ public List Positions { get; private set; }
+ public List Normals { get; private set; }
+ public List UV { get; private set; }
+ public List Tangents { get; private set; }
+ public List BoneWeights { get; private set; }
+
+ public List SubMeshes
+ {
+ get;
+ private set;
+ }
+
+ public List BindPoses { get; private set; }
+ public List Bones { get; private set; }
+
+ public List BlendShapes { get; private set; }
+ public void AddBlendShapesToMesh(Mesh mesh)
+ {
+ Dictionary map = new Dictionary();
+
+ foreach (var x in BlendShapes)
+ {
+ BlendShape bs = null;
+ if (!map.TryGetValue(x.Name, out bs))
+ {
+ bs = new BlendShape();
+ bs.Positions = new Vector3[Positions.Count];
+ bs.Normals = new Vector3[Normals.Count];
+ bs.Tangents = new Vector3[Tangents.Count];
+ bs.Name = x.Name;
+ bs.FrameWeight = x.FrameWeight;
+ map.Add(x.Name, bs);
+ }
+
+ var j = x.VertexOffset;
+ for (int i = 0; i < x.Positions.Length; ++i, ++j)
+ {
+ bs.Positions[j] = x.Positions[i];
+ bs.Normals[j] = x.Normals[i];
+ bs.Tangents[j] = x.Tangents[i];
+ }
+ }
+
+ foreach (var kv in map)
+ {
+ //Debug.LogFormat("AddBlendShapeFrame: {0}", kv.Key);
+ mesh.AddBlendShapeFrame(kv.Key, kv.Value.FrameWeight,
+ kv.Value.Positions, kv.Value.Normals, kv.Value.Tangents);
+ }
+ }
+
+ public MeshIntegrator()
+ {
+// Renderers = new List();
+
+ Positions = new List();
+ Normals = new List();
+ UV = new List();
+ Tangents = new List();
+ BoneWeights = new List();
+
+ SubMeshes = new List();
+
+ BindPoses = new List();
+ Bones = new List();
+
+ BlendShapes = new List();
+ }
+
+ static BoneWeight AddBoneIndexOffset(BoneWeight bw, int boneIndexOffset)
+ {
+ if (bw.weight0 > 0) bw.boneIndex0 += boneIndexOffset;
+ if (bw.weight1 > 0) bw.boneIndex1 += boneIndexOffset;
+ if (bw.weight2 > 0) bw.boneIndex2 += boneIndexOffset;
+ if (bw.weight3 > 0) bw.boneIndex3 += boneIndexOffset;
+ return bw;
+ }
+
+ public void Push(MeshRenderer renderer)
+ {
+ var meshFilter = renderer.GetComponent();
+ if (meshFilter == null)
+ {
+ Debug.LogWarningFormat("{0} has no mesh filter", renderer.name);
+ return;
+ }
+ var mesh = meshFilter.sharedMesh;
+ if (mesh == null)
+ {
+ Debug.LogWarningFormat("{0} has no mesh", renderer.name);
+ return;
+ }
+
+ var indexOffset = Positions.Count;
+ var boneIndexOffset = Bones.Count;
+
+ Positions.AddRange(mesh.vertices
+ .Select(x => renderer.transform.TransformPoint(x))
+ );
+ Normals.AddRange(mesh.normals
+ .Select(x => renderer.transform.TransformVector(x))
+ );
+ UV.AddRange(mesh.uv);
+ Tangents.AddRange(mesh.tangents
+ .Select(t =>
+ {
+ var v = renderer.transform.TransformVector(t.x, t.y, t.z);
+ return new Vector4(v.x, v.y, v.z, t.w);
+ })
+ );
+
+ var self = renderer.transform;
+ var bone = self.parent;
+ if (bone == null)
+ {
+ Debug.LogWarningFormat("{0} is root gameobject.", self.name);
+ return;
+ }
+ var bindpose = bone.worldToLocalMatrix;
+
+ BoneWeights.AddRange(Enumerable.Range(0, mesh.vertices.Length)
+ .Select(x => new BoneWeight()
+ {
+ boneIndex0 = Bones.Count,
+ weight0 = 1,
+ })
+ );
+
+ BindPoses.Add(bindpose);
+ Bones.Add(bone);
+
+ for (int i = 0; i < mesh.subMeshCount; ++i)
+ {
+ var indices = mesh.GetIndices(i).Select(x => x + indexOffset);
+ var mat = renderer.sharedMaterials[i];
+ var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat));
+ if (sameMaterialSubMeshIndex >= 0)
+ {
+ SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices);
+ }
+ else
+ {
+ SubMeshes.Add(new SubMesh
+ {
+ Indices = indices.ToList(),
+ Material = mat,
+ });
+ }
+ }
+ }
+
+ public void Push(SkinnedMeshRenderer renderer)
+ {
+ var mesh = renderer.sharedMesh;
+ if (mesh == null)
+ {
+ Debug.LogWarningFormat("{0} has no mesh", renderer.name);
+ return;
+ }
+
+// Renderers.Add(renderer);
+
+ var indexOffset = Positions.Count;
+ var boneIndexOffset = Bones.Count;
+
+ Positions.AddRange(mesh.vertices);
+ Normals.AddRange(mesh.normals);
+ UV.AddRange(mesh.uv);
+ Tangents.AddRange(mesh.tangents);
+
+ if (mesh.vertexCount == mesh.boneWeights.Length)
+ {
+ BoneWeights.AddRange(mesh.boneWeights.Select(x => AddBoneIndexOffset(x, boneIndexOffset)).ToArray());
+ BindPoses.AddRange(mesh.bindposes);
+ Bones.AddRange(renderer.bones);
+ }
+ else
+ {
+ // Bone Count 0 の SkinnedMeshRenderer
+ var rigidBoneWeight = new BoneWeight
+ {
+ boneIndex0 = boneIndexOffset,
+ weight0 = 1f,
+ };
+ BoneWeights.AddRange(Enumerable.Range(0, mesh.vertexCount).Select(x => rigidBoneWeight).ToArray());
+ BindPoses.Add(renderer.transform.localToWorldMatrix);
+ Bones.Add(renderer.transform);
+ }
+
+ for (int i = 0; i < mesh.subMeshCount; ++i)
+ {
+ var indices = mesh.GetIndices(i).Select(x => x + indexOffset);
+ var mat = renderer.sharedMaterials[i];
+ var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat));
+ if (sameMaterialSubMeshIndex >= 0)
+ {
+ SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices);
+ }
+ else
+ {
+ SubMeshes.Add(new SubMesh
+ {
+ Indices = indices.ToList(),
+ Material = mat,
+ });
+ }
+ }
+
+ for (int i = 0; i < mesh.blendShapeCount; ++i)
+ {
+ var positions = (Vector3[])mesh.vertices.Clone();
+ var normals = (Vector3[])mesh.normals.Clone();
+ var tangents = mesh.tangents.Select(x => (Vector3)x).ToArray();
+
+ mesh.GetBlendShapeFrameVertices(i, 0, positions, normals, tangents);
+ BlendShapes.Add(new BlendShape
+ {
+ VertexOffset = indexOffset,
+ FrameWeight = mesh.GetBlendShapeFrameWeight(i, 0),
+ Name = mesh.GetBlendShapeName(i),
+ Positions = positions,
+ Normals = normals,
+ Tangents = tangents,
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegrator.cs.meta b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegrator.cs.meta
new file mode 100644
index 000000000..15921c899
--- /dev/null
+++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegrator.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 547dd57b50bf4820a570336659345084
+timeCreated: 1560168946
\ No newline at end of file
diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegratorUtility.cs b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegratorUtility.cs
new file mode 100644
index 000000000..e9ab1dc38
--- /dev/null
+++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegratorUtility.cs
@@ -0,0 +1,231 @@
+using System.Collections.Generic;
+using System.Linq;
+using UniGLTF;
+using UnityEngine;
+
+namespace VRM
+{
+ public static class MeshIntegratorUtility
+ {
+ [System.Serializable]
+ public class MeshIntegrationResult
+ {
+ public List SourceSkinnedMeshRenderers = new List();
+ public List SourceMeshRenderers = new List();
+ public SkinnedMeshRenderer IntegratedRenderer;
+ }
+
+ public static bool IntegrateRuntime(GameObject vrmRootObject)
+ {
+ if (vrmRootObject == null) return false;
+ var proxy = vrmRootObject.GetComponent();
+ if (proxy == null) return false;
+ var avatar = proxy.BlendShapeAvatar;
+ if (avatar == null) return false;
+ var clips = avatar.Clips;
+
+ var results = Integrate(vrmRootObject, clips);
+ if (results.Any(x => x.IntegratedRenderer == null)) return false;
+
+ foreach (var result in results)
+ {
+ foreach (var renderer in result.SourceSkinnedMeshRenderers)
+ {
+ Object.Destroy(renderer);
+ }
+
+ foreach (var renderer in result.SourceMeshRenderers)
+ {
+ Object.Destroy(renderer);
+ }
+ }
+
+ return true;
+ }
+
+ public static List Integrate(GameObject root, List blendshapeClips)
+ {
+ var result = new List();
+
+ var withoutBlendShape = IntegrateInternal(root, onlyBlendShapeRenderers: false);
+ if (withoutBlendShape.IntegratedRenderer != null)
+ {
+ result.Add(withoutBlendShape);
+ }
+
+ var onlyBlendShape = IntegrateInternal(root, onlyBlendShapeRenderers: true);
+ if (onlyBlendShape.IntegratedRenderer != null)
+ {
+ result.Add(onlyBlendShape);
+ FollowBlendshapeRendererChange(blendshapeClips, onlyBlendShape, root);
+ }
+
+ return result;
+ }
+
+ private 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;
+ }
+ }
+ }
+
+ private static MeshIntegrationResult IntegrateInternal(GameObject go, bool onlyBlendShapeRenderers)
+ {
+ var result = new MeshIntegrationResult();
+
+#if UNITY_2017_3_OR_NEWER
+#else
+ return result;
+#endif
+
+ var meshNode = new GameObject();
+ if (onlyBlendShapeRenderers)
+ {
+ meshNode.name = "MeshIntegrator(BlendShape)";
+ }
+ else
+ {
+ meshNode.name = "MeshIntegrator";
+ }
+ meshNode.transform.SetParent(go.transform, false);
+
+ // レンダラから情報を集める
+ var integrator = new MeshIntegrator();
+
+ if (onlyBlendShapeRenderers)
+ {
+ foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, true))
+ {
+ integrator.Push(x);
+ result.SourceSkinnedMeshRenderers.Add(x);
+ }
+ }
+ else
+ {
+ foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, false))
+ {
+ integrator.Push(x);
+ result.SourceSkinnedMeshRenderers.Add(x);
+ }
+
+ foreach (var x in EnumerateMeshRenderer(go.transform))
+ {
+ integrator.Push(x);
+ result.SourceMeshRenderers.Add(x);
+ }
+ }
+
+ var mesh = new Mesh();
+ mesh.name = "integrated";
+
+ if (integrator.Positions.Count > ushort.MaxValue)
+ {
+#if UNITY_2017_3_OR_NEWER
+ Debug.LogFormat("exceed 65535 vertices: {0}", integrator.Positions.Count);
+ mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
+#else
+ throw new NotImplementedException(String.Format("exceed 65535 vertices: {0}", integrator.Positions.Count.ToString()));
+#endif
+ }
+
+ mesh.vertices = integrator.Positions.ToArray();
+ mesh.normals = integrator.Normals.ToArray();
+ mesh.uv = integrator.UV.ToArray();
+ mesh.tangents = integrator.Tangents.ToArray();
+ mesh.boneWeights = integrator.BoneWeights.ToArray();
+ mesh.subMeshCount = integrator.SubMeshes.Count;
+ for (var i = 0; i < integrator.SubMeshes.Count; ++i)
+ {
+ mesh.SetIndices(integrator.SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i);
+ }
+ mesh.bindposes = integrator.BindPoses.ToArray();
+
+ if (onlyBlendShapeRenderers)
+ {
+ integrator.AddBlendShapesToMesh(mesh);
+ }
+
+ var integrated = meshNode.AddComponent();
+ integrated.sharedMesh = mesh;
+ integrated.sharedMaterials = integrator.SubMeshes.Select(x => x.Material).ToArray();
+ integrated.bones = integrator.Bones.ToArray();
+ result.IntegratedRenderer = integrated;
+
+ return result;
+ }
+
+ public static IEnumerable EnumerateSkinnedMeshRenderer(Transform root, bool hasBlendShape)
+ {
+ foreach (var x in Traverse(root))
+ {
+ var renderer = x.GetComponent();
+ if (renderer != null &&
+ renderer.gameObject.activeInHierarchy &&
+ renderer.sharedMesh != null &&
+ renderer.enabled &&
+ renderer.sharedMesh.blendShapeCount > 0 == hasBlendShape)
+ {
+ yield return renderer;
+ }
+ }
+ }
+
+ public static IEnumerable EnumerateMeshRenderer(Transform root)
+ {
+ foreach (var x in Traverse(root))
+ {
+ var renderer = x.GetComponent();
+ var filter = x.GetComponent();
+
+ if (renderer != null &&
+ filter != null &&
+ renderer.gameObject.activeInHierarchy &&
+ filter.sharedMesh != null)
+ {
+ yield return renderer;
+ }
+ }
+ }
+
+ private static IEnumerable Traverse(Transform parent)
+ {
+ if (parent.gameObject.activeSelf)
+ {
+ yield return parent;
+
+ foreach (Transform child in parent)
+ {
+ foreach (var x in Traverse(child))
+ {
+ yield return x;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegratorUtility.cs.meta b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegratorUtility.cs.meta
new file mode 100644
index 000000000..b9075e71b
--- /dev/null
+++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/MeshIntegratorUtility.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a982d9d30c0145038245b0214dc2f2e4
+timeCreated: 1560190306
\ No newline at end of file