SubmeshSplitter.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. #if UNITY_EDITOR
  5. using UnityEditor;
  6. #endif
  7. namespace LunarCatsStudio.SuperCombiner
  8. {
  9. /// <summary>
  10. /// Submesh splitter.
  11. /// A helper class that offers possibility to split submeshes into separate meshes
  12. /// </summary>
  13. public class SubmeshSplitter {
  14. /// <summary>
  15. /// Extract a specific submesh at a given index from a mesh and return it as a new Mesh
  16. /// </summary>
  17. /// <param name="mesh">The mesh containing subMeshes</param>
  18. /// <param name="index">The index of the subMesh to extract</param>
  19. /// <returns></returns>
  20. public static Mesh ExtractSubmesh(Mesh mesh, int index)
  21. {
  22. // Create the new mesh corresponding to the submesh at index i
  23. Mesh extractedSubMesh = CreateMeshFromSubmesh(mesh, new int[] { index }, index);
  24. // Return the extracted subMesh
  25. return extractedSubMesh;
  26. }
  27. /// <summary>
  28. /// Extract all the submeshes at given indexes into a separate mesh in a new GameObject
  29. /// </summary>
  30. /// <param name="meshFilter"></param>
  31. /// <param name="subMeshesIndex">List of ths submeshes indexes to extract</param>
  32. /// <param name="index">Only used to name the new created mesh</param>
  33. /// <returns>Returns a new GameObject with MeshRenderer and MeshFilter components.</returns>
  34. public static MeshRenderer SplitSubmeshes(MeshFilter meshFilter, int[] subMeshesIndex, int index)
  35. {
  36. if(subMeshesIndex.Length == 0)
  37. {
  38. Logger.Instance.AddLog("SuperCombiner", "Could not split submeshes of mesh " + meshFilter + " because indexes is null", Logger.LogLevel.LOG_ERROR);
  39. return null;
  40. }
  41. else if(meshFilter.sharedMesh == null)
  42. {
  43. Logger.Instance.AddLog("SuperCombiner", "Could not split submeshes of mesh " + meshFilter + " because it has no mesh", Logger.LogLevel.LOG_ERROR);
  44. return null;
  45. }
  46. Mesh mesh = meshFilter.sharedMesh;
  47. Material[] materials = meshFilter.GetComponent<MeshRenderer>().sharedMaterials;
  48. Material[] splitMaterials = new Material[subMeshesIndex.Length];
  49. for(int i=0; i<subMeshesIndex.Length; i++)
  50. {
  51. splitMaterials[i] = materials[subMeshesIndex[0]];
  52. }
  53. // Create the new mesh corresponding to the submesh at index i
  54. Mesh newMesh = CreateMeshFromSubmesh(mesh, subMeshesIndex, index);
  55. // Generate target GameObject with MeshFilter and MeshRenderer
  56. GameObject go = GenerateGameObject(meshFilter.transform, false, meshFilter.gameObject.name, newMesh, splitMaterials);
  57. // Return the new meshRenderer component
  58. return go.GetComponent<MeshRenderer>();
  59. }
  60. /// <summary>
  61. /// Extract all the submeshes at given indexes into a separate mesh in a new GameObject
  62. /// </summary>
  63. /// <param name="skinnedMesh"></param>
  64. /// <param name="subMeshesIndex"></param>
  65. /// <param name="index"></param>
  66. /// <returns></returns>
  67. public static SkinnedMeshRenderer SplitSubmeshes(SkinnedMeshRenderer skinnedMesh, int[] subMeshesIndex, int index)
  68. {
  69. if (subMeshesIndex.Length == 0)
  70. {
  71. Logger.Instance.AddLog("SuperCombiner", "Could not split submeshes of mesh " + skinnedMesh + " because indexes is null", Logger.LogLevel.LOG_ERROR);
  72. return null;
  73. }
  74. else if (skinnedMesh.sharedMesh == null)
  75. {
  76. Logger.Instance.AddLog("SuperCombiner", "Could not split submeshes of mesh " + skinnedMesh + " because it has no mesh", Logger.LogLevel.LOG_ERROR);
  77. return null;
  78. }
  79. Mesh mesh = skinnedMesh.sharedMesh;
  80. Material[] materials = skinnedMesh.sharedMaterials;
  81. Material[] splitMaterials = new Material[subMeshesIndex.Length];
  82. for (int i = 0; i < subMeshesIndex.Length; i++)
  83. {
  84. splitMaterials[i] = materials[subMeshesIndex[0]];
  85. }
  86. // Create the new mesh corresponding to the submesh at index i
  87. Mesh newMesh = CreateMeshFromSubmesh(mesh, subMeshesIndex, index);
  88. // Generate target GameObject with MeshFilter and MeshRenderer
  89. GameObject go = GenerateGameObject(skinnedMesh.transform, true, skinnedMesh.gameObject.name, newMesh, splitMaterials);
  90. // Return the new meshRenderer component
  91. return go.GetComponent<SkinnedMeshRenderer>();
  92. }
  93. /// <summary>
  94. /// Creates the mesh corresponding to a specific submesh.
  95. /// </summary>
  96. /// <param name="mesh"></param>
  97. /// <param name="subMeshesIndex"></param>
  98. /// <param name="index"></param>
  99. /// <returns></returns>
  100. private static Mesh CreateMeshFromSubmesh(Mesh mesh, int[] subMeshesIndex, int index)
  101. {
  102. Vector3[] vertices = mesh.vertices;
  103. Vector3[] normals = mesh.normals;
  104. Vector4[] tangents = mesh.tangents;
  105. Color[] colors = mesh.colors;
  106. Vector2[] uvs = mesh.uv;
  107. Vector2[] uvs2 = mesh.uv2;
  108. Mesh newMesh = new Mesh();
  109. newMesh.name = mesh.name + "_" + index;
  110. newMesh.vertices = vertices;
  111. newMesh.normals = normals;
  112. newMesh.tangents = tangents;
  113. newMesh.colors = colors;
  114. newMesh.uv = uvs;
  115. newMesh.uv2 = uvs2;
  116. newMesh.subMeshCount = subMeshesIndex.Length;
  117. for (int i=0; i<subMeshesIndex.Length; i++)
  118. {
  119. int[] triangles = mesh.GetTriangles(subMeshesIndex[i]);
  120. int[] indices = mesh.GetIndices(subMeshesIndex[i]);
  121. MeshTopology topology = mesh.GetTopology(subMeshesIndex[i]);
  122. newMesh.SetIndices(indices, topology, i);
  123. newMesh.SetTriangles(triangles, i);
  124. }
  125. #if UNITY_EDITOR
  126. // Optimize the vertices
  127. MeshUtility.Optimize(newMesh);
  128. #endif
  129. return newMesh;
  130. }
  131. /// <summary>
  132. /// Generates a new game object.
  133. /// </summary>
  134. /// <param name="parent"></param>
  135. /// <param name="skinnedMesh"></param>
  136. /// <param name="name_p"></param>
  137. /// <param name="newMesh_p"></param>
  138. /// <param name="mat"></param>
  139. /// <returns></returns>
  140. private static GameObject GenerateGameObject(Transform parent, bool skinnedMesh, string name_p, Mesh newMesh_p, Material[] mat)
  141. {
  142. GameObject go = new GameObject(name_p);
  143. go.transform.SetParent(parent);
  144. go.transform.localPosition = Vector3.zero;
  145. go.transform.localRotation = Quaternion.identity;
  146. go.transform.localScale = Vector3.one;
  147. MeshFilter meshFilter = go.AddComponent<MeshFilter>();
  148. meshFilter.mesh = newMesh_p;
  149. if(skinnedMesh)
  150. {
  151. SkinnedMeshRenderer meshRenderer = go.AddComponent<SkinnedMeshRenderer>();
  152. meshRenderer.materials = mat;
  153. } else
  154. {
  155. MeshRenderer meshRenderer = go.AddComponent<MeshRenderer>();
  156. meshRenderer.materials = mat;
  157. }
  158. return go;
  159. }
  160. }
  161. }