CartesianMixerState.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. using System.Text;
  4. using UnityEngine;
  5. namespace Animancer
  6. {
  7. /// <summary>[Pro-Only]
  8. /// An <see cref="AnimancerState"/> which blends an array of other states together based on a two dimensional
  9. /// parameter and thresholds using Gradient Band Interpolation.
  10. /// </summary>
  11. /// <remarks>
  12. /// This mixer type is similar to the 2D Freeform Cartesian Blend Type in Mecanim Blend Trees.
  13. /// <para></para>
  14. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/blending/mixers">Mixers</see>
  15. /// </remarks>
  16. /// https://kybernetik.com.au/animancer/api/Animancer/CartesianMixerState
  17. ///
  18. public class CartesianMixerState : MixerState<Vector2>
  19. {
  20. /************************************************************************************************************************/
  21. /// <summary><see cref="MixerState{TParameter}.Parameter"/>.x.</summary>
  22. public float ParameterX
  23. {
  24. get => Parameter.x;
  25. set => Parameter = new Vector2(value, Parameter.y);
  26. }
  27. /// <summary><see cref="MixerState{TParameter}.Parameter"/>.y.</summary>
  28. public float ParameterY
  29. {
  30. get => Parameter.y;
  31. set => Parameter = new Vector2(Parameter.x, value);
  32. }
  33. /************************************************************************************************************************/
  34. /// <inheritdoc/>
  35. public override string GetParameterError(Vector2 value)
  36. => value.IsFinite() ? null : $"value.x and value.y {Strings.MustBeFinite}";
  37. /************************************************************************************************************************/
  38. /// <summary>Precalculated values to speed up the recalculation of weights.</summary>
  39. private Vector2[][] _BlendFactors;
  40. /// <summary>Indicates whether the <see cref="_BlendFactors"/> need to be recalculated.</summary>
  41. private bool _BlendFactorsDirty = true;
  42. /************************************************************************************************************************/
  43. /// <summary>
  44. /// Called whenever the thresholds are changed. Indicates that the internal blend factors need to be
  45. /// recalculated and calls <see cref="ForceRecalculateWeights"/>.
  46. /// </summary>
  47. public override void OnThresholdsChanged()
  48. {
  49. _BlendFactorsDirty = true;
  50. base.OnThresholdsChanged();
  51. }
  52. /************************************************************************************************************************/
  53. /// <summary>
  54. /// Recalculates the weights of all <see cref="MixerState.ChildStates"/> based on the current value of the
  55. /// <see cref="MixerState{TParameter}.Parameter"/> and the <see cref="MixerState{TParameter}._Thresholds"/>.
  56. /// </summary>
  57. protected override void ForceRecalculateWeights()
  58. {
  59. WeightsAreDirty = false;
  60. var childCount = ChildCount;
  61. if (childCount == 0)
  62. {
  63. return;
  64. }
  65. else if (childCount == 1)
  66. {
  67. var state = GetChild(0);
  68. state.Weight = 1;
  69. return;
  70. }
  71. CalculateBlendFactors(childCount);
  72. float totalWeight = 0;
  73. for (int i = 0; i < childCount; i++)
  74. {
  75. var state = GetChild(i);
  76. if (state == null)
  77. continue;
  78. var blendFactors = _BlendFactors[i];
  79. var threshold = GetThreshold(i);
  80. var thresholdToParameter = Parameter - threshold;
  81. float weight = 1;
  82. for (int j = 0; j < childCount; j++)
  83. {
  84. if (j == i || GetChild(j) == null)
  85. continue;
  86. var newWeight = 1 - Vector2.Dot(thresholdToParameter, blendFactors[j]);
  87. if (weight > newWeight)
  88. weight = newWeight;
  89. }
  90. if (weight < 0.01f)
  91. weight = 0;
  92. state.Weight = weight;
  93. totalWeight += weight;
  94. }
  95. NormalizeWeights(totalWeight);
  96. }
  97. /************************************************************************************************************************/
  98. private void CalculateBlendFactors(int childCount)
  99. {
  100. if (!_BlendFactorsDirty)
  101. return;
  102. _BlendFactorsDirty = false;
  103. // Resize the precalculated values.
  104. if (AnimancerUtilities.SetLength(ref _BlendFactors, childCount))
  105. {
  106. for (int i = 0; i < childCount; i++)
  107. _BlendFactors[i] = new Vector2[childCount];
  108. }
  109. // Calculate the blend factors between each combination of thresholds.
  110. for (int i = 0; i < childCount; i++)
  111. {
  112. var blendFactors = _BlendFactors[i];
  113. var thresholdI = GetThreshold(i);
  114. var j = i + 1;
  115. for (; j < childCount; j++)
  116. {
  117. var thresholdIToJ = GetThreshold(j) - thresholdI;
  118. thresholdIToJ *= 1f / thresholdIToJ.sqrMagnitude;
  119. // Each factor is used in [i][j] with it's opposite in [j][i].
  120. blendFactors[j] = thresholdIToJ;
  121. _BlendFactors[j][i] = -thresholdIToJ;
  122. }
  123. }
  124. }
  125. /************************************************************************************************************************/
  126. /// <inheritdoc/>
  127. public override void AppendParameter(StringBuilder text, Vector2 parameter)
  128. {
  129. text.Append('(')
  130. .Append(parameter.x)
  131. .Append(", ")
  132. .Append(parameter.y)
  133. .Append(')');
  134. }
  135. /************************************************************************************************************************/
  136. #region Inspector
  137. /************************************************************************************************************************/
  138. /// <inheritdoc/>
  139. protected override int ParameterCount => 2;
  140. /// <inheritdoc/>
  141. protected override string GetParameterName(int index)
  142. {
  143. switch (index)
  144. {
  145. case 0: return "Parameter X";
  146. case 1: return "Parameter Y";
  147. default: throw new ArgumentOutOfRangeException(nameof(index));
  148. }
  149. }
  150. /// <inheritdoc/>
  151. protected override AnimatorControllerParameterType GetParameterType(int index) => AnimatorControllerParameterType.Float;
  152. /// <inheritdoc/>
  153. protected override object GetParameterValue(int index)
  154. {
  155. switch (index)
  156. {
  157. case 0: return ParameterX;
  158. case 1: return ParameterY;
  159. default: throw new ArgumentOutOfRangeException(nameof(index));
  160. }
  161. }
  162. /// <inheritdoc/>
  163. protected override void SetParameterValue(int index, object value)
  164. {
  165. switch (index)
  166. {
  167. case 0: ParameterX = (float)value; break;
  168. case 1: ParameterY = (float)value; break;
  169. default: throw new ArgumentOutOfRangeException(nameof(index));
  170. }
  171. }
  172. /************************************************************************************************************************/
  173. #endregion
  174. /************************************************************************************************************************/
  175. }
  176. }