SplineHelpers.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using UnityEngine;
  2. namespace Cinemachine.Utility
  3. {
  4. public static class SplineHelpers
  5. {
  6. public static Vector3 Bezier3(
  7. float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
  8. {
  9. t = Mathf.Clamp01(t);
  10. float d = 1f - t;
  11. return d * d * d * p0 + 3f * d * d * t * p1
  12. + 3f * d * t * t * p2 + t * t * t * p3;
  13. }
  14. public static Vector3 BezierTangent3(
  15. float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
  16. {
  17. t = Mathf.Clamp01(t);
  18. return (-3f * p0 + 9f * p1 - 9f * p2 + 3f * p3) * (t * t)
  19. + (6f * p0 - 12f * p1 + 6f * p2) * t
  20. - 3f * p0 + 3f * p1;
  21. }
  22. public static float Bezier1(float t, float p0, float p1, float p2, float p3)
  23. {
  24. t = Mathf.Clamp01(t);
  25. float d = 1f - t;
  26. return d * d * d * p0 + 3f * d * d * t * p1
  27. + 3f * d * t * t * p2 + t * t * t * p3;
  28. }
  29. public static float BezierTangent1(
  30. float t, float p0, float p1, float p2, float p3)
  31. {
  32. t = Mathf.Clamp01(t);
  33. return (-3f * p0 + 9f * p1 - 9f * p2 + 3f * p3) * t * t
  34. + (6f * p0 - 12f * p1 + 6f * p2) * t
  35. - 3f * p0 + 3f * p1;
  36. }
  37. public static void ComputeSmoothControlPoints(
  38. ref Vector4[] knot, ref Vector4[] ctrl1, ref Vector4[] ctrl2)
  39. {
  40. int numPoints = knot.Length;
  41. if (numPoints <= 2)
  42. {
  43. if (numPoints == 2)
  44. {
  45. ctrl1[0] = Vector4.Lerp(knot[0], knot[1], 0.33333f);
  46. ctrl2[0] = Vector4.Lerp(knot[0], knot[1], 0.66666f);
  47. }
  48. else if (numPoints == 1)
  49. ctrl1[0] = ctrl2[0] = knot[0];
  50. return;
  51. }
  52. var a = new float[numPoints];
  53. var b = new float[numPoints];
  54. var c = new float[numPoints];
  55. var r = new float[numPoints];
  56. for (int axis = 0; axis < 4; ++axis)
  57. {
  58. int n = numPoints - 1;
  59. // Linear into the first segment
  60. a[0] = 0;
  61. b[0] = 2;
  62. c[0] = 1;
  63. r[0] = knot[0][axis] + 2 * knot[1][axis];
  64. // Internal segments
  65. for (int i = 1; i < n - 1; ++i)
  66. {
  67. a[i] = 1;
  68. b[i] = 4;
  69. c[i] = 1;
  70. r[i] = 4 * knot[i][axis] + 2 * knot[i+1][axis];
  71. }
  72. // Linear out of the last segment
  73. a[n - 1] = 2;
  74. b[n - 1] = 7;
  75. c[n - 1] = 0;
  76. r[n - 1] = 8 * knot[n - 1][axis] + knot[n][axis];
  77. // Solve with Thomas algorithm
  78. for (int i = 1; i < n; ++i)
  79. {
  80. float m = a[i] / b[i-1];
  81. b[i] = b[i] - m * c[i-1];
  82. r[i] = r[i] - m * r[i-1];
  83. }
  84. // Compute ctrl1
  85. ctrl1[n-1][axis] = r[n-1] / b[n-1];
  86. for (int i = n - 2; i >= 0; --i)
  87. ctrl1[i][axis] = (r[i] - c[i] * ctrl1[i + 1][axis]) / b[i];
  88. // Compute ctrl2 from ctrl1
  89. for (int i = 0; i < n; i++)
  90. ctrl2[i][axis] = 2 * knot[i + 1][axis] - ctrl1[i + 1][axis];
  91. ctrl2[n - 1][axis] = 0.5f * (knot[n][axis] + ctrl1[n - 1][axis]);
  92. }
  93. }
  94. public static void ComputeSmoothControlPointsLooped(
  95. ref Vector4[] knot, ref Vector4[] ctrl1, ref Vector4[] ctrl2)
  96. {
  97. int numPoints = knot.Length;
  98. if (numPoints < 2)
  99. {
  100. if (numPoints == 1)
  101. ctrl1[0] = ctrl2[0] = knot[0];
  102. return;
  103. }
  104. int margin = Mathf.Min(4, numPoints-1);
  105. Vector4[] knotLooped = new Vector4[numPoints + 2 * margin];
  106. Vector4[] ctrl1Looped = new Vector4[numPoints + 2 * margin];
  107. Vector4[] ctrl2Looped = new Vector4[numPoints + 2 * margin];
  108. for (int i = 0; i < margin; ++i)
  109. {
  110. knotLooped[i] = knot[numPoints-(margin-i)];
  111. knotLooped[numPoints+margin+i] = knot[i];
  112. }
  113. for (int i = 0; i < numPoints; ++i)
  114. knotLooped[i + margin] = knot[i];
  115. ComputeSmoothControlPoints(ref knotLooped, ref ctrl1Looped, ref ctrl2Looped);
  116. for (int i = 0; i < numPoints; ++i)
  117. {
  118. ctrl1[i] = ctrl1Looped[i + margin];
  119. ctrl2[i] = ctrl2Looped[i + margin];
  120. }
  121. }
  122. }
  123. }