MB3_MeshCombinerSimpleBones.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. using System.Collections;
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace DigitalOpus.MB.Core
  6. {
  7. public partial class MB3_MeshCombinerSingle : MB3_MeshCombiner
  8. {
  9. public class MB3_MeshCombinerSimpleBones
  10. {
  11. MB3_MeshCombinerSingle combiner;
  12. List<MB3_MeshCombinerSingle.MB_DynamicGameObject>[] boneIdx2dgoMap = null;
  13. HashSet<int> boneIdxsToDelete = new HashSet<int>();
  14. HashSet<MB3_MeshCombinerSingle.BoneAndBindpose> bonesToAdd = new HashSet<MB3_MeshCombinerSingle.BoneAndBindpose>();
  15. Dictionary<BoneAndBindpose, int> bone2idx = new Dictionary<BoneAndBindpose, int>();
  16. public MB3_MeshCombinerSimpleBones(MB3_MeshCombinerSingle cm)
  17. {
  18. combiner = cm;
  19. }
  20. public HashSet<MB3_MeshCombinerSingle.BoneAndBindpose> GetBonesToAdd()
  21. {
  22. return bonesToAdd;
  23. }
  24. public int GetNumBonesToDelete()
  25. {
  26. return boneIdxsToDelete.Count;
  27. }
  28. private bool _didSetup = false;
  29. public void BuildBoneIdx2DGOMapIfNecessary(int[] _goToDelete)
  30. {
  31. _didSetup = false;
  32. if (combiner.settings.renderType == MB_RenderType.skinnedMeshRenderer)
  33. {
  34. if (_goToDelete.Length > 0)
  35. {
  36. boneIdx2dgoMap = _buildBoneIdx2dgoMap();
  37. }
  38. for (int i = 0; i < combiner.bones.Length; i++)
  39. {
  40. BoneAndBindpose bn = new BoneAndBindpose(combiner.bones[i], combiner.bindPoses[i]);
  41. bone2idx.Add(bn, i);
  42. }
  43. _didSetup = true;
  44. }
  45. }
  46. public void FindBonesToDelete(MB_DynamicGameObject dgo)
  47. {
  48. Debug.Assert(_didSetup);
  49. Debug.Assert(combiner.settings.renderType == MB_RenderType.skinnedMeshRenderer);
  50. // We could be working with adding and deleting smr body parts from the same rig. Different smrs will share
  51. // the same bones. Track if we need to delete a bone or not.
  52. for (int j = 0; j < dgo.indexesOfBonesUsed.Length; j++)
  53. {
  54. int idxOfUsedBone = dgo.indexesOfBonesUsed[j];
  55. List<MB_DynamicGameObject> dgosThatUseBone = boneIdx2dgoMap[idxOfUsedBone];
  56. if (dgosThatUseBone.Contains(dgo))
  57. {
  58. dgosThatUseBone.Remove(dgo);
  59. if (dgosThatUseBone.Count == 0)
  60. {
  61. boneIdxsToDelete.Add(idxOfUsedBone);
  62. }
  63. }
  64. }
  65. }
  66. public int GetNewBonesLength()
  67. {
  68. return combiner.bindPoses.Length + bonesToAdd.Count - boneIdxsToDelete.Count;
  69. }
  70. public void CollectBonesToAddForDGO(MB_DynamicGameObject dgo, Renderer r, MeshChannelsCache meshChannelCache)
  71. {
  72. Debug.Assert(_didSetup, "Need to setup first.");
  73. Debug.Assert(combiner.settings.renderType == MB_RenderType.skinnedMeshRenderer);
  74. // We could be working with adding and deleting smr body parts from the same rig. Different smrs will share
  75. // the same bones.
  76. //cache the bone data that we will be adding.
  77. Matrix4x4[] dgoBindPoses = dgo._tmpCachedBindposes = meshChannelCache.GetBindposes(r);
  78. BoneWeight[] dgoBoneWeights = dgo._tmpCachedBoneWeights = meshChannelCache.GetBoneWeights(r, dgo.numVerts);
  79. Transform[] dgoBones = dgo._tmpCachedBones = combiner._getBones(r);
  80. // The mesh being added may not use all bones on the rig. Find the bones actually used.
  81. int[] usedBoneIdx2srcMeshBoneIdx;
  82. {
  83. /*
  84. HashSet<int> usedBones = new HashSet<int>();
  85. for (int j = 0; j < dgoBoneWeights.Length; j++)
  86. {
  87. usedBones.Add(dgoBoneWeights[j].boneIndex0);
  88. usedBones.Add(dgoBoneWeights[j].boneIndex1);
  89. usedBones.Add(dgoBoneWeights[j].boneIndex2);
  90. usedBones.Add(dgoBoneWeights[j].boneIndex3);
  91. }
  92. usedBoneIdx2srcMeshBoneIdx = new int[usedBones.Count];
  93. usedBones.CopyTo(usedBoneIdx2srcMeshBoneIdx);
  94. */
  95. }
  96. {
  97. usedBoneIdx2srcMeshBoneIdx = new int[dgoBones.Length];
  98. for (int i = 0; i < usedBoneIdx2srcMeshBoneIdx.Length; i++) usedBoneIdx2srcMeshBoneIdx[i] = i;
  99. }
  100. // For each bone see if it exists in the bones array (with the same bindpose.
  101. for (int i = 0; i < usedBoneIdx2srcMeshBoneIdx.Length; i++)
  102. {
  103. bool foundInBonesList = false;
  104. int bidx;
  105. int dgoBoneIdx = usedBoneIdx2srcMeshBoneIdx[i];
  106. BoneAndBindpose bb = new BoneAndBindpose(dgoBones[dgoBoneIdx], dgoBindPoses[dgoBoneIdx]);
  107. if (bone2idx.TryGetValue(bb, out bidx))
  108. {
  109. if (dgoBones[dgoBoneIdx] == combiner.bones[bidx] && !boneIdxsToDelete.Contains(bidx))
  110. {
  111. if (dgoBindPoses[dgoBoneIdx] == combiner.bindPoses[bidx])
  112. {
  113. foundInBonesList = true;
  114. }
  115. }
  116. }
  117. if (!foundInBonesList)
  118. {
  119. if (!bonesToAdd.Contains(bb))
  120. {
  121. bonesToAdd.Add(bb);
  122. }
  123. }
  124. }
  125. dgo._tmpIndexesOfSourceBonesUsed = usedBoneIdx2srcMeshBoneIdx;
  126. }
  127. private List<MB3_MeshCombinerSingle.MB_DynamicGameObject>[] _buildBoneIdx2dgoMap()
  128. {
  129. List<MB3_MeshCombinerSingle.MB_DynamicGameObject>[] boneIdx2dgoMap = new List<MB3_MeshCombinerSingle.MB_DynamicGameObject>[combiner.bones.Length];
  130. for (int i = 0; i < boneIdx2dgoMap.Length; i++) boneIdx2dgoMap[i] = new List<MB3_MeshCombinerSingle.MB_DynamicGameObject>();
  131. // build the map of bone indexes to objects that use them
  132. for (int i = 0; i < combiner.mbDynamicObjectsInCombinedMesh.Count; i++)
  133. {
  134. MB3_MeshCombinerSingle.MB_DynamicGameObject dgo = combiner.mbDynamicObjectsInCombinedMesh[i];
  135. for (int j = 0; j < dgo.indexesOfBonesUsed.Length; j++)
  136. {
  137. boneIdx2dgoMap[dgo.indexesOfBonesUsed[j]].Add(dgo);
  138. }
  139. }
  140. return boneIdx2dgoMap;
  141. }
  142. public void CopyBonesWeAreKeepingToNewBonesArrayAndAdjustBWIndexes(Transform[] nbones, Matrix4x4[] nbindPoses, BoneWeight[] nboneWeights, int totalDeleteVerts)
  143. {
  144. // bones are copied separately because some dgos share bones
  145. if (boneIdxsToDelete.Count > 0)
  146. {
  147. int[] boneIdxsToDel = new int[boneIdxsToDelete.Count];
  148. boneIdxsToDelete.CopyTo(boneIdxsToDel);
  149. Array.Sort(boneIdxsToDel);
  150. //bones are being moved in bones array so need to do some remapping
  151. int[] oldBonesIndex2newBonesIndexMap = new int[combiner.bones.Length];
  152. int newIdx = 0;
  153. int indexInDeleteList = 0;
  154. //bones were deleted so we need to rebuild bones and bind poses
  155. //and build a map of old bone indexes to new bone indexes
  156. //do this by copying old to new skipping ones we are deleting
  157. for (int i = 0; i < combiner.bones.Length; i++)
  158. {
  159. if (indexInDeleteList < boneIdxsToDel.Length &&
  160. boneIdxsToDel[indexInDeleteList] == i)
  161. {
  162. //we are deleting this bone so skip its index
  163. indexInDeleteList++;
  164. oldBonesIndex2newBonesIndexMap[i] = -1;
  165. }
  166. else
  167. {
  168. oldBonesIndex2newBonesIndexMap[i] = newIdx;
  169. nbones[newIdx] = combiner.bones[i];
  170. nbindPoses[newIdx] = combiner.bindPoses[i];
  171. newIdx++;
  172. }
  173. }
  174. //adjust the indexes on the boneWeights
  175. int numVertKeeping = combiner.boneWeights.Length - totalDeleteVerts;
  176. {
  177. for (int i = 0; i < numVertKeeping; i++)
  178. {
  179. BoneWeight bw = nboneWeights[i];
  180. bw.boneIndex0 = oldBonesIndex2newBonesIndexMap[bw.boneIndex0];
  181. bw.boneIndex1 = oldBonesIndex2newBonesIndexMap[bw.boneIndex1];
  182. bw.boneIndex2 = oldBonesIndex2newBonesIndexMap[bw.boneIndex2];
  183. bw.boneIndex3 = oldBonesIndex2newBonesIndexMap[bw.boneIndex3];
  184. nboneWeights[i] = bw;
  185. }
  186. }
  187. /*
  188. unsafe
  189. {
  190. fixed (BoneWeight* boneWeightFirstPtr = &nboneWeights[0])
  191. {
  192. BoneWeight* boneWeightPtr = boneWeightFirstPtr;
  193. for (int i = 0; i < numVertKeeping; i++)
  194. {
  195. boneWeightPtr->boneIndex0 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex0];
  196. boneWeightPtr->boneIndex1 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex1];
  197. boneWeightPtr->boneIndex2 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex2];
  198. boneWeightPtr->boneIndex3 = oldBonesIndex2newBonesIndexMap[boneWeightPtr->boneIndex3];
  199. boneWeightPtr++;
  200. }
  201. }
  202. }
  203. */
  204. //adjust the bone indexes on the dgos from old to new
  205. for (int i = 0; i < combiner.mbDynamicObjectsInCombinedMesh.Count; i++)
  206. {
  207. MB_DynamicGameObject dgo = combiner.mbDynamicObjectsInCombinedMesh[i];
  208. for (int j = 0; j < dgo.indexesOfBonesUsed.Length; j++)
  209. {
  210. dgo.indexesOfBonesUsed[j] = oldBonesIndex2newBonesIndexMap[dgo.indexesOfBonesUsed[j]];
  211. }
  212. }
  213. }
  214. else
  215. { //no bones are moving so can simply copy bones from old to new
  216. Array.Copy(combiner.bones, nbones, combiner.bones.Length);
  217. Array.Copy(combiner.bindPoses, nbindPoses, combiner.bindPoses.Length);
  218. }
  219. }
  220. public static void AddBonesToNewBonesArrayAndAdjustBWIndexes(MB3_MeshCombinerSingle combiner, MB_DynamicGameObject dgo, Renderer r, int vertsIdx,
  221. Transform[] nbones, BoneWeight[] nboneWeights, MeshChannelsCache meshChannelCache)
  222. {
  223. //Renderer r = MB_Utility.GetRenderer(go);
  224. Transform[] dgoBones = dgo._tmpCachedBones;
  225. Matrix4x4[] dgoBindPoses = dgo._tmpCachedBindposes;
  226. BoneWeight[] dgoBoneWeights = dgo._tmpCachedBoneWeights;
  227. int[] srcIndex2combinedIndexMap = new int[dgoBones.Length];
  228. for (int j = 0; j < dgo._tmpIndexesOfSourceBonesUsed.Length; j++)
  229. {
  230. int dgoBoneIdx = dgo._tmpIndexesOfSourceBonesUsed[j];
  231. for (int k = 0; k < nbones.Length; k++)
  232. {
  233. if (dgoBones[dgoBoneIdx] == nbones[k])
  234. {
  235. if (dgoBindPoses[dgoBoneIdx] == combiner.bindPoses[k])
  236. {
  237. srcIndex2combinedIndexMap[dgoBoneIdx] = k;
  238. break;
  239. }
  240. }
  241. }
  242. }
  243. //remap the bone weights for this dgo
  244. //build a list of usedBones, can't trust dgoBones because it contains all bones in the rig
  245. for (int j = 0; j < dgoBoneWeights.Length; j++)
  246. {
  247. int newVertIdx = vertsIdx + j;
  248. nboneWeights[newVertIdx].boneIndex0 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex0];
  249. nboneWeights[newVertIdx].boneIndex1 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex1];
  250. nboneWeights[newVertIdx].boneIndex2 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex2];
  251. nboneWeights[newVertIdx].boneIndex3 = srcIndex2combinedIndexMap[dgoBoneWeights[j].boneIndex3];
  252. nboneWeights[newVertIdx].weight0 = dgoBoneWeights[j].weight0;
  253. nboneWeights[newVertIdx].weight1 = dgoBoneWeights[j].weight1;
  254. nboneWeights[newVertIdx].weight2 = dgoBoneWeights[j].weight2;
  255. nboneWeights[newVertIdx].weight3 = dgoBoneWeights[j].weight3;
  256. }
  257. // repurposing the _tmpIndexesOfSourceBonesUsed since
  258. //we don't need it anymore and this saves a memory allocation . remap the indexes that point to source bones to combined bones.
  259. for (int j = 0; j < dgo._tmpIndexesOfSourceBonesUsed.Length; j++)
  260. {
  261. dgo._tmpIndexesOfSourceBonesUsed[j] = srcIndex2combinedIndexMap[dgo._tmpIndexesOfSourceBonesUsed[j]];
  262. }
  263. dgo.indexesOfBonesUsed = dgo._tmpIndexesOfSourceBonesUsed;
  264. dgo._tmpIndexesOfSourceBonesUsed = null;
  265. dgo._tmpCachedBones = null;
  266. dgo._tmpCachedBindposes = null;
  267. dgo._tmpCachedBoneWeights = null;
  268. //check original bones and bindPoses
  269. /*
  270. for (int j = 0; j < dgo.indexesOfBonesUsed.Length; j++) {
  271. Transform bone = bones[dgo.indexesOfBonesUsed[j]];
  272. Matrix4x4 bindpose = bindPoses[dgo.indexesOfBonesUsed[j]];
  273. bool found = false;
  274. for (int k = 0; k < dgo._originalBones.Length; k++) {
  275. if (dgo._originalBones[k] == bone && dgo._originalBindPoses[k] == bindpose) {
  276. found = true;
  277. }
  278. }
  279. if (!found) Debug.LogError("A Mismatch between original bones and bones array. " + dgo.name);
  280. }
  281. */
  282. }
  283. }
  284. }
  285. }