summaryrefslogtreecommitdiff
path: root/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs')
-rw-r--r--Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs286
1 files changed, 286 insertions, 0 deletions
diff --git a/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs
new file mode 100644
index 00000000..045879c6
--- /dev/null
+++ b/Assets/ThirdParty/VRM/MeshUtility/Editor/MeshUtility.cs
@@ -0,0 +1,286 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEngine;
+using UnityEditor;
+
+namespace MeshUtility
+{
+ public class MeshUtility
+ {
+ public const string MENU_PARENT = "Mesh Utility/";
+ public const int MENU_PRIORITY = 11;
+
+ private const string ASSET_SUFFIX = ".mesh.asset";
+ private const string MENU_NAME = MENU_PARENT + "MeshSeparator";
+ private static readonly Vector3 ZERO_MOVEMENT = Vector3.zero;
+
+ public static Object GetPrefab(GameObject instance)
+ {
+#if UNITY_2018_2_OR_NEWER
+ return PrefabUtility.GetCorrespondingObjectFromSource(instance);
+#else
+ return PrefabUtility.GetPrefabParent(go);
+#endif
+ }
+
+ private enum BlendShapeLogic
+ {
+ WithBlendShape,
+ WithoutBlendShape,
+ }
+
+ [MenuItem(MENU_NAME, validate = true)]
+ private static bool ShowLogValidation()
+ {
+ if (Selection.activeTransform == null)
+ return false;
+ else
+ return true;
+ }
+
+ [MenuItem(MENU_NAME, priority = 2)]
+ public static void SeparateSkinnedMeshContainedBlendShape()
+ {
+ var go = Selection.activeTransform.gameObject;
+
+ if (go.GetComponentsInChildren<SkinnedMeshRenderer>().Length > 0)
+ {
+ SeparationProcessing(go);
+ go.SetActive(false);
+ }
+ else
+ {
+ EditorUtility.DisplayDialog("Error", "No skinnedMeshRenderer contained", "ok");
+ }
+ }
+
+ [MenuItem("Mesh Utility/MeshSeparator Docs", priority = MeshUtility.MENU_PRIORITY)]
+ public static void LinkToMeshSeparatorDocs()
+ {
+ Application.OpenURL("https://github.com/vrm-c/UniVRM/tree/master/Assets/MeshUtility");
+ }
+
+ private static void SeparationProcessing(GameObject go)
+ {
+ var outputObject = GameObject.Instantiate(go);
+ var skinnedMeshRenderers = outputObject.GetComponentsInChildren<SkinnedMeshRenderer>();
+ foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
+ {
+ if (skinnedMeshRenderer.sharedMesh.blendShapeCount > 0)
+ {
+ SeparatePolyWithBlendShape(skinnedMeshRenderer);
+ }
+ }
+ }
+
+ private static void SeparatePolyWithBlendShape(SkinnedMeshRenderer skinnedMeshRendererInput)
+ {
+ var indicesUsedByBlendShape = new Dictionary<int, int>();
+ var mesh = skinnedMeshRendererInput.sharedMesh;
+
+ // retrieve the original BlendShape data
+ for (int i = 0; i < mesh.blendShapeCount; ++i)
+ {
+ var deltaVertices = new Vector3[mesh.vertexCount];
+ var deltaNormals = new Vector3[mesh.vertexCount];
+ var deltaTangents = new Vector3[mesh.vertexCount];
+ mesh.GetBlendShapeFrameVertices(i, 0, deltaVertices, deltaNormals, deltaTangents);
+
+ for (int j = 0; j < deltaVertices.Length; j++)
+ {
+ if (!deltaVertices[j].Equals(ZERO_MOVEMENT))
+ {
+ if (!indicesUsedByBlendShape.Values.Contains(j))
+ {
+ indicesUsedByBlendShape.Add(indicesUsedByBlendShape.Count, j);
+ }
+ }
+ }
+ }
+
+ var subMeshCount = mesh.subMeshCount;
+ var submeshesWithBlendShape = new Dictionary<int, int[]>();
+ var submeshesWithoutBlendShape = new Dictionary<int, int[]>();
+ var vertexIndexWithBlendShape = new Dictionary<int, int>();
+ var vertexCounterWithBlendShape = 0;
+ var vertexIndexWithoutBlendShape = new Dictionary<int, int>();
+ var vertexCounterWithoutBlendShape = 0;
+
+ // check blendshape's vertex index from submesh
+ for (int i = 0; i < subMeshCount; i++)
+ {
+ var triangle = mesh.GetTriangles(i);
+ var submeshWithBlendShape = new List<int>();
+ var submeshWithoutBlendShape = new List<int>();
+
+ for (int j = 0; j < triangle.Length; j += 3)
+ {
+ if (indicesUsedByBlendShape.Values.Contains(triangle[j]) ||
+ indicesUsedByBlendShape.Values.Contains(triangle[j + 1]) ||
+ indicesUsedByBlendShape.Values.Contains(triangle[j + 2]))
+ {
+ BuildNewTriangleList(vertexIndexWithBlendShape, triangle, j, submeshWithBlendShape, ref vertexCounterWithBlendShape);
+ }
+ else
+ {
+ BuildNewTriangleList(vertexIndexWithoutBlendShape, triangle, j, submeshWithoutBlendShape, ref vertexCounterWithoutBlendShape);
+ }
+ }
+ if (submeshWithBlendShape.Count > 0)
+ submeshesWithBlendShape.Add(i, submeshWithBlendShape.ToArray());
+ if (submeshWithoutBlendShape.Count > 0)
+ submeshesWithoutBlendShape.Add(i, submeshWithoutBlendShape.ToArray()); ;
+ }
+
+ // check if any BlendShape exists
+ if (submeshesWithoutBlendShape.Count > 0)
+ {
+ // put the mesh without BlendShape in a new SkinnedMeshRenderer
+ var srcGameObject = skinnedMeshRendererInput.gameObject;
+ var srcTransform = skinnedMeshRendererInput.transform.parent;
+ var targetObjectForMeshWithoutBS = GameObject.Instantiate(srcGameObject);
+ targetObjectForMeshWithoutBS.name = srcGameObject.name + "_WithoutBlendShape";
+ targetObjectForMeshWithoutBS.transform.SetParent(srcTransform);
+ var skinnedMeshRendererWithoutBS = targetObjectForMeshWithoutBS.GetComponent<SkinnedMeshRenderer>();
+
+ // build meshes with/without BlendShape
+ BuildNewMesh(skinnedMeshRendererInput, vertexIndexWithBlendShape, submeshesWithBlendShape, BlendShapeLogic.WithBlendShape);
+ BuildNewMesh(skinnedMeshRendererWithoutBS, vertexIndexWithoutBlendShape, submeshesWithoutBlendShape, BlendShapeLogic.WithoutBlendShape);
+ }
+ }
+
+ private static void BuildNewTriangleList(Dictionary<int, int> newVerticesListLookUp, int[] triangleList, int index,
+ List<int> newTriangleList, ref int vertexCounter)
+ {
+ // build new vertex list and triangle list
+ // vertex 1
+ if (!newVerticesListLookUp.Keys.Contains(triangleList[index]))
+ {
+ newVerticesListLookUp.Add(triangleList[index], vertexCounter);
+ newTriangleList.Add(vertexCounter);
+ vertexCounter++;
+ }
+ else
+ {
+ var newVertexIndex = newVerticesListLookUp[triangleList[index]];
+ newTriangleList.Add(newVertexIndex);
+ }
+ // vertex 2
+ if (!newVerticesListLookUp.Keys.Contains(triangleList[index + 1]))
+ {
+ newVerticesListLookUp.Add(triangleList[index + 1], vertexCounter);
+ newTriangleList.Add(vertexCounter);
+ vertexCounter++;
+ }
+ else
+ {
+ var newVertexIndex = newVerticesListLookUp[triangleList[index + 1]];
+ newTriangleList.Add(newVertexIndex);
+ }
+ // vertex 3
+ if (!newVerticesListLookUp.Keys.Contains(triangleList[index + 2]))
+ {
+ newVerticesListLookUp.Add(triangleList[index + 2], vertexCounter);
+ newTriangleList.Add(vertexCounter);
+ vertexCounter++;
+ }
+ else
+ {
+ var newVertexIndex = newVerticesListLookUp[triangleList[index + 2]];
+ newTriangleList.Add(newVertexIndex);
+ }
+ }
+
+ private static void BuildNewMesh(SkinnedMeshRenderer skinnedMeshRenderer, Dictionary<int, int> newIndexLookUpDict,
+ Dictionary<int, int[]> subMeshes, BlendShapeLogic blendShapeLabel)
+ {
+ // get original mesh data
+ var materialList = new List<Material>();
+ skinnedMeshRenderer.GetSharedMaterials(materialList);
+ var mesh = skinnedMeshRenderer.sharedMesh;
+ var meshVertices = mesh.vertices;
+ var meshNormals = mesh.normals;
+ var meshTangents = mesh.tangents;
+ var meshColors = mesh.colors;
+ var meshBoneWeights = mesh.boneWeights;
+ var meshUVs = mesh.uv;
+
+ // build new mesh
+ var materialListNew = new List<Material>();
+ var newMesh = new Mesh();
+
+ if (mesh.vertexCount > ushort.MaxValue)
+ {
+#if UNITY_2017_3_OR_NEWER
+ Debug.LogFormat("exceed 65535 vertices: {0}", mesh.vertexCount);
+ newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
+#else
+ throw new NotImplementedException(String.Format("exceed 65535 vertices: {0}", integrator.Positions.Count.ToString()));
+#endif
+ }
+
+ var newDataLength = newIndexLookUpDict.Count;
+ var newIndexLookUp = newIndexLookUpDict.Keys.ToArray();
+
+ newMesh.vertices = newIndexLookUp.Select(x => meshVertices[x]).ToArray();
+ if (meshNormals.Length > 0) newMesh.normals = newIndexLookUp.Select(x => meshNormals[x]).ToArray();
+ if (meshTangents.Length > 0) newMesh.tangents = newIndexLookUp.Select(x => meshTangents[x]).ToArray();
+ if (meshColors.Length > 0) newMesh.colors = newIndexLookUp.Select(x => meshColors[x]).ToArray();
+ if (meshBoneWeights.Length > 0) newMesh.boneWeights = newIndexLookUp.Select(x => meshBoneWeights[x]).ToArray();
+ if (meshUVs.Length > 0) newMesh.uv = newIndexLookUp.Select(x => meshUVs[x]).ToArray();
+ newMesh.bindposes = mesh.bindposes;
+
+ // add BlendShape data
+ if (blendShapeLabel == BlendShapeLogic.WithBlendShape)
+ {
+ for (int i = 0; i < mesh.blendShapeCount; i++)
+ {
+ // get original BlendShape data
+ var srcVertices = new Vector3[mesh.vertexCount];
+ var srcNormals = new Vector3[mesh.vertexCount];
+ var srcTangents = new Vector3[mesh.vertexCount];
+ mesh.GetBlendShapeFrameVertices(i, 0, srcVertices, srcNormals, srcTangents);
+
+ // declare the size for the destination array
+ var dstVertices = new Vector3[newDataLength];
+ var dstNormals = new Vector3[newDataLength];
+ var dstTangents = new Vector3[newDataLength];
+
+ dstVertices = newIndexLookUp.Select(x => srcVertices[x]).ToArray();
+ dstNormals = newIndexLookUp.Select(x => srcNormals[x]).ToArray();
+ dstTangents = newIndexLookUp.Select(x => srcTangents[x]).ToArray();
+ newMesh.AddBlendShapeFrame(mesh.GetBlendShapeName(i), mesh.GetBlendShapeFrameWeight(i, 0),
+ dstVertices, dstNormals, dstTangents);
+ }
+ }
+
+ newMesh.subMeshCount = subMeshes.Count;
+ var cosMaterialIndex = subMeshes.Keys.ToArray();
+
+ // build material list
+ for (int i = 0; i < subMeshes.Count; i++)
+ {
+ newMesh.SetTriangles(subMeshes[cosMaterialIndex[i]], i);
+ materialListNew.Add(materialList[cosMaterialIndex[i]]);
+ }
+ skinnedMeshRenderer.sharedMaterials = materialListNew.ToArray();
+ skinnedMeshRenderer.sharedMesh = newMesh;
+
+ // save mesh as asset
+ var assetPath = string.Format("{0}{1}", Path.GetFileNameWithoutExtension(mesh.name), ASSET_SUFFIX);
+ Debug.Log(assetPath);
+ if (!string.IsNullOrEmpty((AssetDatabase.GetAssetPath(mesh))))
+ {
+ var directory = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)).Replace("\\", "/");
+ assetPath = string.Format("{0}/{1}{2}", directory, Path.GetFileNameWithoutExtension(mesh.name) + "_" + blendShapeLabel.ToString(), ASSET_SUFFIX);
+ }
+ else
+ {
+ assetPath = string.Format("Assets/{0}{1}", Path.GetFileNameWithoutExtension(mesh.name) + "_" + blendShapeLabel.ToString(), ASSET_SUFFIX);
+ }
+ Debug.LogFormat("CreateAsset: {0}", assetPath);
+ AssetDatabase.CreateAsset(newMesh, assetPath);
+ }
+ }
+} \ No newline at end of file