CartesianMixerState.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System;
  3. using UnityEngine;
  4. namespace Animancer
  5. {
  6. /// <summary>[Pro-Only]
  7. /// An <see cref="AnimancerState"/> which blends an array of other states together based on a two dimensional
  8. /// parameter and thresholds using Gradient Band Interpolation.
  9. /// </summary>
  10. /// <remarks>
  11. /// This mixer type is similar to the 2D Freeform Cartesian Blend Type in Mecanim Blend Trees.
  12. /// <para></para>
  13. /// <strong>Documentation:</strong>
  14. /// <see href="https://kybernetik.com.au/animancer/docs/manual/blending/mixers">
  15. /// Mixers</see>
  16. /// </remarks>
  17. /// https://kybernetik.com.au/animancer/api/Animancer/CartesianMixerState
  18. ///
  19. public class CartesianMixerState : Vector2MixerState,
  20. ICopyable<CartesianMixerState>
  21. {
  22. /************************************************************************************************************************/
  23. /// <summary>Precalculated values to speed up the recalculation of weights.</summary>
  24. private Vector2[][] _BlendFactors;
  25. /// <summary>Indicates whether the <see cref="_BlendFactors"/> need to be recalculated.</summary>
  26. private bool _BlendFactorsAreDirty = true;
  27. /************************************************************************************************************************/
  28. /// <summary>
  29. /// Called whenever the thresholds are changed. Indicates that the internal blend factors need to be
  30. /// recalculated and triggers weight recalculation.
  31. /// </summary>
  32. public override void OnThresholdsChanged()
  33. {
  34. _BlendFactorsAreDirty = true;
  35. base.OnThresholdsChanged();
  36. }
  37. /************************************************************************************************************************/
  38. /// <inheritdoc/>
  39. protected override void ForceRecalculateWeights()
  40. {
  41. var childCount = ChildCount;
  42. if (childCount == 0)
  43. {
  44. return;
  45. }
  46. else if (childCount == 1)
  47. {
  48. Playable.SetChildWeight(ChildStates[0], 1);
  49. return;
  50. }
  51. CalculateBlendFactors(childCount);
  52. float totalWeight = 0;
  53. var weights = GetTemporaryFloatArray(childCount);
  54. for (int i = 0; i < childCount; i++)
  55. {
  56. var state = ChildStates[i];
  57. var blendFactors = _BlendFactors[i];
  58. var threshold = GetThreshold(i);
  59. var thresholdToParameter = Parameter - threshold;
  60. float weight = 1;
  61. for (int j = 0; j < childCount; j++)
  62. {
  63. if (j == i)
  64. continue;
  65. var newWeight = 1 - Vector2.Dot(thresholdToParameter, blendFactors[j]);
  66. if (weight > newWeight)
  67. weight = newWeight;
  68. }
  69. if (weight < 0.01f)
  70. weight = 0;
  71. weights[i] = weight;
  72. totalWeight += weight;
  73. }
  74. NormalizeAndApplyWeights(totalWeight, weights);
  75. }
  76. /************************************************************************************************************************/
  77. private void CalculateBlendFactors(int childCount)
  78. {
  79. if (!_BlendFactorsAreDirty)
  80. return;
  81. _BlendFactorsAreDirty = false;
  82. // Resize the precalculated values.
  83. if (AnimancerUtilities.SetLength(ref _BlendFactors, childCount))
  84. {
  85. for (int i = 0; i < childCount; i++)
  86. _BlendFactors[i] = new Vector2[childCount];
  87. }
  88. // Calculate the blend factors between each combination of thresholds.
  89. for (int i = 0; i < childCount; i++)
  90. {
  91. var blendFactors = _BlendFactors[i];
  92. var thresholdI = GetThreshold(i);
  93. var j = i + 1;
  94. for (; j < childCount; j++)
  95. {
  96. var thresholdIToJ = GetThreshold(j) - thresholdI;
  97. #if UNITY_ASSERTIONS
  98. if (thresholdIToJ == default)
  99. {
  100. MarkAsUsed(this);
  101. throw new ArgumentException(
  102. $"Mixer has multiple identical thresholds.\n{this.GetDescription()}");
  103. }
  104. #endif
  105. thresholdIToJ /= thresholdIToJ.sqrMagnitude;
  106. // Each factor is used in [i][j] with it's opposite in [j][i].
  107. blendFactors[j] = thresholdIToJ;
  108. _BlendFactors[j][i] = -thresholdIToJ;
  109. }
  110. }
  111. }
  112. /************************************************************************************************************************/
  113. /// <inheritdoc/>
  114. public override AnimancerState Clone(CloneContext context)
  115. {
  116. var clone = new CartesianMixerState();
  117. clone.CopyFrom(this, context);
  118. return clone;
  119. }
  120. /************************************************************************************************************************/
  121. /// <inheritdoc/>
  122. public sealed override void CopyFrom(Vector2MixerState copyFrom, CloneContext context)
  123. => this.CopyFromBase(copyFrom, context);
  124. /// <inheritdoc/>
  125. public virtual void CopyFrom(CartesianMixerState copyFrom, CloneContext context)
  126. {
  127. _BlendFactorsAreDirty = copyFrom._BlendFactorsAreDirty;
  128. if (!_BlendFactorsAreDirty)
  129. _BlendFactors = copyFrom._BlendFactors;
  130. base.CopyFrom(copyFrom, context);
  131. }
  132. /************************************************************************************************************************/
  133. }
  134. }