MeshCombiner.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using HierarchyDict = System.Collections.Generic.Dictionary<string, UnityEngine.Transform>;
  4. using BoneTransformDict = System.Collections.Generic.Dictionary<string, utils.Tuple<UnityEngine.Transform, string>>;
  5. namespace utils
  6. {
  7. public class MeshCombiner
  8. {
  9. #region Operations
  10. //! Combine mesh.
  11. /*!
  12. \return combined mesh instance.
  13. */
  14. public static GameObject Combine(List<SkinnedMeshRenderer> SkinnedRenderers)
  15. {
  16. // Generated GO
  17. GameObject final_mesh_go = new GameObject("Mesh");
  18. // Dummy parent holder
  19. GameObject dummy_parent = new GameObject("DummyParent");
  20. // All available bones
  21. var all_bones = new BoneTransformDict();
  22. // Traverse through all skinned mesh renderers
  23. foreach(var renderer in SkinnedRenderers)
  24. {
  25. var renderer_bones = renderer.bones;
  26. foreach (var bone in renderer_bones)
  27. {
  28. // Bone doesn't exist, add it
  29. if (!all_bones.ContainsKey(bone.name))
  30. all_bones[bone.name] = new utils.Tuple<Transform, string>(bone, bone.parent.name);
  31. }
  32. }
  33. var combineInstanceArrays = new Dictionary<Material, List<CombineInstance>>();
  34. var bone_weights = new Dictionary<Mesh, BoneWeight[]>();
  35. // Map between bone name and index
  36. var added_bones = new Dictionary<string, int>();
  37. // List of child objects holding the skinned mesh renderers to be
  38. // destroyed when finished
  39. var child_objects_to_destroy = new List<GameObject>();
  40. int bone_index = 0;
  41. foreach(var renderer in SkinnedRenderers)
  42. {
  43. child_objects_to_destroy.Add(renderer.transform.parent.gameObject);
  44. var renderer_bones = renderer.bones;
  45. // Add all bones as first and save the indices of them
  46. foreach (var bone in renderer_bones)
  47. {
  48. // Bone not yet added
  49. if (!added_bones.ContainsKey(bone.name))
  50. added_bones[bone.name] = bone_index++;
  51. }
  52. // Adjust bone weights indices based on real indices of bones
  53. var bone_weights_list = new BoneWeight[renderer.sharedMesh.boneWeights.Length];
  54. var renderer_bone_weights = renderer.sharedMesh.boneWeights;
  55. for (int i = 0; i < renderer_bone_weights.Length; ++i)
  56. {
  57. BoneWeight current_bone_weight = renderer_bone_weights[i];
  58. current_bone_weight.boneIndex0 = added_bones[renderer_bones[current_bone_weight.boneIndex0].name];
  59. current_bone_weight.boneIndex2 = added_bones[renderer_bones[current_bone_weight.boneIndex2].name];
  60. current_bone_weight.boneIndex3 = added_bones[renderer_bones[current_bone_weight.boneIndex3].name];
  61. current_bone_weight.boneIndex1 = added_bones[renderer_bones[current_bone_weight.boneIndex1].name];
  62. bone_weights_list[i] = current_bone_weight;
  63. }
  64. bone_weights[renderer.sharedMesh] = bone_weights_list;
  65. // Handle bad input
  66. if (renderer.sharedMaterials.Length != renderer.sharedMesh.subMeshCount)
  67. {
  68. Debug.LogError("Mismatch between material count and submesh count. Is this the correct MeshRenderer?");
  69. continue;
  70. }
  71. // Prepare stuff for mesh combination with same materials
  72. for (int i = 0; i < renderer.sharedMesh.subMeshCount; i++)
  73. {
  74. // Material not in dict, add it
  75. if (!combineInstanceArrays.ContainsKey(renderer.sharedMaterials[i]))
  76. combineInstanceArrays[renderer.sharedMaterials[i]] = new List<CombineInstance>();
  77. var actual_mat_list = combineInstanceArrays[renderer.sharedMaterials[i]];
  78. // Add new instance
  79. var combine_instance = new CombineInstance();
  80. combine_instance.transform = renderer.transform.localToWorldMatrix;
  81. combine_instance.subMeshIndex = i;
  82. combine_instance.mesh = renderer.sharedMesh;
  83. actual_mat_list.Add(combine_instance);
  84. }
  85. // No need to use it anymore
  86. renderer.enabled = false;
  87. }
  88. var bones_hierarchy = new HierarchyDict();
  89. // Recreate bone structure
  90. foreach (var bone in all_bones)
  91. {
  92. // Bone not processed, process it
  93. if (!bones_hierarchy.ContainsKey(bone.Key))
  94. AddParent(bone.Key, bones_hierarchy, all_bones, dummy_parent);
  95. }
  96. // Create bone array from preprocessed dict
  97. var bones = new Transform[added_bones.Count];
  98. foreach (var bone in added_bones)
  99. bones[bone.Value] = bones_hierarchy[bone.Key];
  100. // Get the root bone
  101. Transform root_bone = bones[0];
  102. while (root_bone.parent != null)
  103. {
  104. // Get parent
  105. if (bones_hierarchy.ContainsKey(root_bone.parent.name))
  106. root_bone = root_bone.parent;
  107. else
  108. break;
  109. }
  110. // Create skinned mesh renderer GO
  111. GameObject combined_mesh_go = new GameObject("Combined");
  112. combined_mesh_go.transform.parent = final_mesh_go.transform;
  113. combined_mesh_go.transform.localPosition = Vector3.zero;
  114. // Fill bind poses
  115. var bind_poses = new Matrix4x4[bones.Length];
  116. for (int i = 0; i < bones.Length; ++i)
  117. bind_poses[i] = bones[i].worldToLocalMatrix * combined_mesh_go.transform.localToWorldMatrix;
  118. // Need to move it to new GO
  119. root_bone.parent = final_mesh_go.transform;
  120. // Combine meshes into one
  121. var combined_new_mesh = new Mesh();
  122. var combined_vertices = new List<Vector3>();
  123. var combined_uvs = new List<Vector2>();
  124. var combined_indices = new List<int[]>();
  125. var combined_bone_weights = new List<BoneWeight>();
  126. var combined_materials = new Material[combineInstanceArrays.Count];
  127. var vertex_offset_map = new Dictionary<Mesh, int>();
  128. int vertex_index_offset = 0;
  129. int current_material_index = 0;
  130. foreach (var combine_instance in combineInstanceArrays)
  131. {
  132. combined_materials[current_material_index++] = combine_instance.Key;
  133. var submesh_indices = new List<int>();
  134. // Process meshes for each material
  135. foreach (var combine in combine_instance.Value)
  136. {
  137. // Update vertex offset for current mesh
  138. if (!vertex_offset_map.ContainsKey(combine.mesh))
  139. {
  140. // Add vertices for mesh
  141. combined_vertices.AddRange(combine.mesh.vertices);
  142. // Set uvs
  143. combined_uvs.AddRange(combine.mesh.uv);
  144. // Add weights
  145. combined_bone_weights.AddRange(bone_weights[combine.mesh]);
  146. vertex_offset_map[combine.mesh] = vertex_index_offset;
  147. vertex_index_offset += combine.mesh.vertexCount;
  148. }
  149. int vertex_current_offset = vertex_offset_map[combine.mesh];
  150. var indices = combine.mesh.GetTriangles(combine.subMeshIndex);
  151. // Need to "shift" indices
  152. for (int k = 0; k < indices.Length; ++k)
  153. indices[k] += vertex_current_offset;
  154. submesh_indices.AddRange(indices);
  155. }
  156. // Push indices for given submesh
  157. combined_indices.Add(submesh_indices.ToArray());
  158. }
  159. combined_new_mesh.vertices = combined_vertices.ToArray();
  160. combined_new_mesh.uv = combined_uvs.ToArray();
  161. combined_new_mesh.boneWeights = combined_bone_weights.ToArray();
  162. combined_new_mesh.subMeshCount = combined_materials.Length;
  163. for (int i = 0; i < combined_indices.Count; ++i)
  164. combined_new_mesh.SetTriangles(combined_indices[i], i);
  165. // Create mesh renderer
  166. SkinnedMeshRenderer combined_skin_mesh_renderer = combined_mesh_go.AddComponent<SkinnedMeshRenderer>();
  167. combined_skin_mesh_renderer.sharedMesh = combined_new_mesh;
  168. combined_skin_mesh_renderer.bones = bones;
  169. combined_skin_mesh_renderer.rootBone = root_bone;
  170. combined_skin_mesh_renderer.sharedMesh.bindposes = bind_poses;
  171. combined_skin_mesh_renderer.sharedMesh.RecalculateNormals();
  172. combined_skin_mesh_renderer.sharedMesh.RecalculateBounds();
  173. combined_skin_mesh_renderer.sharedMaterials = combined_materials;
  174. // Destroy children
  175. foreach (var child in child_objects_to_destroy)
  176. GameObject.DestroyImmediate(child);
  177. // Destroy dummy parent
  178. GameObject.DestroyImmediate(dummy_parent);
  179. return final_mesh_go;
  180. }
  181. static void AddParent(string BoneName, HierarchyDict BoneHierarchy, BoneTransformDict AllBones, GameObject DummyParent)
  182. {
  183. Transform actual_bone = null;
  184. // Must be bone
  185. if (AllBones.ContainsKey(BoneName))
  186. {
  187. var bone_tuple = AllBones[BoneName];
  188. // Add parent recursively if not added
  189. if (!BoneHierarchy.ContainsKey(bone_tuple._2))
  190. {
  191. AddParent(bone_tuple._2, BoneHierarchy, AllBones, DummyParent);
  192. // Unparent all children of parents
  193. Unparent(BoneHierarchy[bone_tuple._2], DummyParent);
  194. }
  195. bone_tuple._1.parent = BoneHierarchy[bone_tuple._2];
  196. actual_bone = bone_tuple._1;
  197. }
  198. BoneHierarchy[BoneName] = actual_bone;
  199. }
  200. static void Unparent(Transform Parent, GameObject DummyParent)
  201. {
  202. if (Parent != null)
  203. {
  204. var unparent_list = new List<Transform>();
  205. foreach (Transform child in Parent.transform)
  206. unparent_list.Add(child);
  207. foreach (var child in unparent_list)
  208. child.parent = DummyParent.transform;
  209. }
  210. }
  211. #endregion
  212. }
  213. public struct Tuple<T1, T2>
  214. {
  215. public readonly T1 _1;
  216. public readonly T2 _2;
  217. public Tuple(T1 T1_, T2 T2_)
  218. {
  219. _1 = T1_;
  220. _2 = T2_;
  221. }
  222. }
  223. }