EditableBoneWeightUtility.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. using UnityEngine;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace UnityEditor.U2D.Animation
  5. {
  6. internal struct BoneWeightData : IComparable<BoneWeightData>
  7. {
  8. public int boneIndex;
  9. public float weight;
  10. public int CompareTo(BoneWeightData other)
  11. {
  12. return other.weight.CompareTo(weight);
  13. }
  14. }
  15. internal static class EditableBoneWeightUtility
  16. {
  17. private static List<BoneWeightData> s_BoneWeightDataList = new List<BoneWeightData>();
  18. private static EditableBoneWeight s_LerpFirst = new EditableBoneWeight();
  19. private static EditableBoneWeight s_LerpSecond = new EditableBoneWeight();
  20. private static EditableBoneWeight s_LerpResult = new EditableBoneWeight();
  21. public static EditableBoneWeight CreateFromBoneWeight(BoneWeight boneWeight)
  22. {
  23. EditableBoneWeight editableBoneWeight = new EditableBoneWeight();
  24. editableBoneWeight.SetFromBoneWeight(boneWeight);
  25. editableBoneWeight.UnifyChannelsWithSameBoneIndex();
  26. return editableBoneWeight;
  27. }
  28. public static void SetFromBoneWeight(this EditableBoneWeight editableBoneWeight, BoneWeight boneWeight)
  29. {
  30. editableBoneWeight.Clamp(4, false);
  31. while (editableBoneWeight.Count < 4)
  32. editableBoneWeight.AddChannel(0, 0f, false);
  33. for (var i = 0; i < 4; ++i)
  34. {
  35. var weight = boneWeight.GetWeight(i);
  36. editableBoneWeight[i].boneIndex = boneWeight.GetBoneIndex(i);
  37. editableBoneWeight[i].weight = weight;
  38. editableBoneWeight[i].enabled = weight > 0f;
  39. }
  40. }
  41. public static BoneWeight ToBoneWeight(this EditableBoneWeight editableBoneWeight, bool sortByWeight)
  42. {
  43. var boneWeight = new BoneWeight();
  44. if (editableBoneWeight.Count > 0)
  45. {
  46. s_BoneWeightDataList.Clear();
  47. s_BoneWeightDataList.Capacity = editableBoneWeight.Count;
  48. for (var i = 0; i < editableBoneWeight.Count; ++i)
  49. {
  50. s_BoneWeightDataList.Add(new BoneWeightData()
  51. {
  52. boneIndex = editableBoneWeight[i].boneIndex,
  53. weight = editableBoneWeight[i].weight
  54. });
  55. }
  56. if (sortByWeight)
  57. s_BoneWeightDataList.Sort();
  58. var count = Mathf.Min(editableBoneWeight.Count, 4);
  59. for (var i = 0; i < count; ++i)
  60. {
  61. BoneWeightExtensions.SetBoneIndex(ref boneWeight, i, s_BoneWeightDataList[i].boneIndex);
  62. BoneWeightExtensions.SetWeight(ref boneWeight, i, s_BoneWeightDataList[i].weight);
  63. }
  64. }
  65. return boneWeight;
  66. }
  67. public static bool ContainsBoneIndex(this EditableBoneWeight editableBoneWeight, int boneIndex)
  68. {
  69. return GetChannelFromBoneIndex(editableBoneWeight, boneIndex) > -1;
  70. }
  71. public static int GetChannelFromBoneIndex(this EditableBoneWeight editableBoneWeight, int boneIndex)
  72. {
  73. for (int i = 0; i < editableBoneWeight.Count; ++i)
  74. if (editableBoneWeight[i].enabled && editableBoneWeight[i].boneIndex == boneIndex)
  75. return i;
  76. return -1;
  77. }
  78. public static void Clamp(this EditableBoneWeight editableBoneWeight, int numChannels, bool sortChannels = true)
  79. {
  80. if (sortChannels)
  81. editableBoneWeight.Sort();
  82. while (editableBoneWeight.Count > numChannels)
  83. editableBoneWeight.RemoveChannel(numChannels);
  84. }
  85. public static void ValidateChannels(this EditableBoneWeight editableBoneWeight)
  86. {
  87. for (int i = 0; i < editableBoneWeight.Count; ++i)
  88. {
  89. var weight = editableBoneWeight[i].weight;
  90. if (!editableBoneWeight[i].enabled)
  91. weight = 0f;
  92. weight = Mathf.Clamp01(weight);
  93. editableBoneWeight[i].weight = weight;
  94. }
  95. }
  96. public static float Sum(this EditableBoneWeight editableBoneWeight)
  97. {
  98. var sum = 0f;
  99. for (var i = 0; i < editableBoneWeight.Count; ++i)
  100. if (editableBoneWeight[i].enabled)
  101. sum += editableBoneWeight[i].weight;
  102. return sum;
  103. }
  104. public static void Normalize(this EditableBoneWeight editableBoneWeight)
  105. {
  106. ValidateChannels(editableBoneWeight);
  107. var sum = editableBoneWeight.Sum();
  108. if (sum == 0f || sum == 1f)
  109. return;
  110. var sumInv = 1f / sum;
  111. for (var i = 0; i < editableBoneWeight.Count; ++i)
  112. if (editableBoneWeight[i].enabled)
  113. editableBoneWeight[i].weight *= sumInv;
  114. }
  115. public static void CompensateOtherChannels(this EditableBoneWeight editableBoneWeight, int masterChannel)
  116. {
  117. ValidateChannels(editableBoneWeight);
  118. var validChannelCount = 0;
  119. var sum = 0f;
  120. for (int i = 0; i < editableBoneWeight.Count; ++i)
  121. {
  122. if (i != masterChannel && editableBoneWeight[i].enabled)
  123. {
  124. sum += editableBoneWeight[i].weight;
  125. ++validChannelCount;
  126. }
  127. }
  128. if (validChannelCount == 0)
  129. return;
  130. var targetSum = 1f - editableBoneWeight[masterChannel].weight;
  131. for (var i = 0; i < editableBoneWeight.Count; ++i)
  132. {
  133. if (i != masterChannel && editableBoneWeight[i].enabled)
  134. {
  135. if (sum == 0f)
  136. editableBoneWeight[i].weight = targetSum / validChannelCount;
  137. else
  138. editableBoneWeight[i].weight *= targetSum / sum;
  139. }
  140. }
  141. }
  142. public static void UnifyChannelsWithSameBoneIndex(this EditableBoneWeight editableBoneWeight)
  143. {
  144. for (var i = 0; i < editableBoneWeight.Count; ++i)
  145. {
  146. if (!editableBoneWeight[i].enabled)
  147. continue;
  148. bool weightChanged = false;
  149. for (var j = i + 1; j < editableBoneWeight.Count; ++j)
  150. {
  151. if (editableBoneWeight[j].boneIndex == editableBoneWeight[i].boneIndex)
  152. {
  153. weightChanged = true;
  154. editableBoneWeight[i].weight += editableBoneWeight[j].weight;
  155. editableBoneWeight[j].enabled = false;
  156. }
  157. }
  158. if (weightChanged)
  159. editableBoneWeight.CompensateOtherChannels(i);
  160. }
  161. }
  162. public static void FilterChannels(this EditableBoneWeight editableBoneWeight, float weightTolerance)
  163. {
  164. for (var i = 0; i < editableBoneWeight.Count; ++i)
  165. {
  166. if (editableBoneWeight[i].weight <= weightTolerance)
  167. {
  168. editableBoneWeight[i].boneIndex = 0;
  169. editableBoneWeight[i].weight = 0f;
  170. editableBoneWeight[i].enabled = false;
  171. }
  172. }
  173. }
  174. public static BoneWeight Lerp(BoneWeight first, BoneWeight second, float t)
  175. {
  176. s_LerpFirst.SetFromBoneWeight(first);
  177. s_LerpSecond.SetFromBoneWeight(second);
  178. Lerp(s_LerpFirst, s_LerpSecond, ref s_LerpResult, t);
  179. return s_LerpResult.ToBoneWeight(true);
  180. }
  181. private static void Lerp(EditableBoneWeight first, EditableBoneWeight second, ref EditableBoneWeight result, float t)
  182. {
  183. result.Clear();
  184. foreach (BoneWeightChannel channel in first)
  185. {
  186. if (!channel.enabled)
  187. continue;
  188. var weight = channel.weight * (1f - t);
  189. if (weight > 0f)
  190. result.AddChannel(channel.boneIndex, weight, true);
  191. }
  192. foreach (BoneWeightChannel channel in second)
  193. {
  194. if (!channel.enabled)
  195. continue;
  196. var weight = channel.weight * t;
  197. if (weight > 0f)
  198. result.AddChannel(channel.boneIndex, weight, true);
  199. }
  200. result.UnifyChannelsWithSameBoneIndex();
  201. result.Clamp(4);
  202. if (result.Sum() > 1f)
  203. result.Normalize();
  204. result.FilterChannels(0f);
  205. }
  206. }
  207. }