BezierCurve.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. using System;
  2. using UnityEngine;
  3. namespace Unity.VRTemplate
  4. {
  5. /// <summary>
  6. /// Draws a bezier curve from a starting point transform to an end point transform
  7. /// </summary>
  8. public class BezierCurve : MonoBehaviour
  9. {
  10. /// <summary>
  11. /// If the view scale changes more than this amount, then the line width will be updated causing the line to be rebuilt.
  12. /// </summary>
  13. const float k_ViewerScaleChangeThreshold = 0.1f;
  14. /// <summary>
  15. /// The time within the frame that the curve will be updated.
  16. /// </summary>
  17. /// <seealso cref="UnityEngine.XR.Interaction.Toolkit.XRBaseController.UpdateType"/>
  18. public enum UpdateType
  19. {
  20. /// <summary>
  21. /// Sample at both update and directly before rendering. For smooth tracking,
  22. /// we recommend using this value as it will provide the lowest input latency for the device.
  23. /// </summary>
  24. UpdateAndBeforeRender,
  25. /// <summary>
  26. /// Only sample input during the update phase of the frame.
  27. /// </summary>
  28. Update,
  29. /// <summary>
  30. /// Only sample input directly before rendering.
  31. /// </summary>
  32. BeforeRender,
  33. }
  34. #pragma warning disable 649
  35. [SerializeField, Tooltip("The time within the frame that the curve will be updated. If this Bezier Curve is attached to a transform that is updating before render, then enabling updates in Before Render will keep the line connected without delay.")]
  36. UpdateType m_UpdateTrackingType = UpdateType.Update;
  37. [SerializeField, Tooltip("The transform that determines the position, handle rotation, and handle scale of the start point of the bezier curve.")]
  38. Transform m_StartPoint;
  39. [SerializeField, Tooltip("The transform that determines the position, handle rotation, and handle scale of the end point of the bezier curve.")]
  40. Transform m_EndPoint;
  41. [SerializeField, Tooltip("Controls the scale factor of the curve's start bezier handle.")]
  42. float m_CurveFactorStart = 1.0f;
  43. [SerializeField, Tooltip("Controls the scale factor of the curve's end bezier handle.")]
  44. float m_CurveFactorEnd = 1.0f;
  45. [SerializeField, Tooltip("Controls the number of segments used to draw the curve.")]
  46. int m_SegmentCount = 50;
  47. [SerializeField, Tooltip("When enabled, the line color gradient will be animated so that an opaque part travels along the line.")]
  48. bool m_Animate;
  49. [SerializeField, Tooltip("If animated, this controls the speed that the animation of the line.")]
  50. float m_AnimSpeed = 0.25f;
  51. [SerializeField, Tooltip("If animated, this color will be the main opaque color of the gradient")]
  52. Color m_GradientKeyColor = new Color(0.1254902f, 0.5882353f, 0.9529412f);
  53. [SerializeField, Tooltip("The line renderer that will draw the curve. If not set it will find a line renderer on this GameObject.")]
  54. LineRenderer m_LineRenderer;
  55. #pragma warning restore 649
  56. Vector3[] m_ControlPoints = new Vector3[4];
  57. float m_Time;
  58. float m_LineWidth;
  59. float m_LastViewerScale;
  60. Vector3 m_LastStartPosition;
  61. Vector3 m_LastEndPosition;
  62. //IProvidesViewerScale IFunctionalitySubscriber<IProvidesViewerScale>.provider { get; set; }
  63. void Awake()
  64. {
  65. if (m_LineRenderer == null)
  66. m_LineRenderer = GetComponent<LineRenderer>();
  67. m_LineWidth = m_LineRenderer.startWidth;
  68. }
  69. void OnEnable()
  70. {
  71. DrawCurve();
  72. Application.onBeforeRender += OnBeforeRender;
  73. }
  74. void OnDisable()
  75. {
  76. Application.onBeforeRender -= OnBeforeRender;
  77. }
  78. void OnBeforeRender()
  79. {
  80. if (m_UpdateTrackingType == UpdateType.BeforeRender || m_UpdateTrackingType == UpdateType.UpdateAndBeforeRender)
  81. DrawCurve();
  82. }
  83. void Update()
  84. {
  85. if (m_UpdateTrackingType == UpdateType.Update || m_UpdateTrackingType == UpdateType.UpdateAndBeforeRender)
  86. DrawCurve();
  87. if (m_Animate)
  88. {
  89. AnimateCurve();
  90. }
  91. }
  92. /// <summary>
  93. /// Updates the line points to draw the bezier curve.
  94. /// </summary>
  95. [ContextMenu("Draw")]
  96. public void DrawCurve()
  97. {
  98. var startPointPosition = m_StartPoint.position;
  99. var endPointPosition = m_EndPoint.position;
  100. if (startPointPosition == m_LastStartPosition &&
  101. endPointPosition == m_LastEndPosition)
  102. return; // Return early if the start and end have not changed to avoid recalculating the curve
  103. var dist = Vector3.Distance(startPointPosition, endPointPosition);
  104. m_ControlPoints[0] = startPointPosition;
  105. m_ControlPoints[1] = startPointPosition + (m_StartPoint.right * (dist * m_CurveFactorStart));
  106. m_ControlPoints[2] = endPointPosition - (m_EndPoint.right * (dist * m_CurveFactorEnd));
  107. m_ControlPoints[3] = endPointPosition;
  108. int segmentCount;
  109. const float smallestCurveLength = 0.0125f;
  110. if (Vector3.Distance(startPointPosition, endPointPosition) < (smallestCurveLength * m_LastViewerScale))
  111. {
  112. segmentCount = 2;
  113. }
  114. else
  115. {
  116. segmentCount = m_SegmentCount;
  117. }
  118. m_LineRenderer.positionCount = segmentCount + 1;
  119. m_LineRenderer.SetPosition(0, m_ControlPoints[0]);
  120. for (var i = 1; i <= segmentCount; i++)
  121. {
  122. var t = i / (float)segmentCount;
  123. var pixel = CalculateCubicBezierPoint(t, m_ControlPoints[0], m_ControlPoints[1], m_ControlPoints[2], m_ControlPoints[3]);
  124. m_LineRenderer.SetPosition(i, pixel);
  125. }
  126. m_LastStartPosition = startPointPosition;
  127. m_LastEndPosition = endPointPosition;
  128. }
  129. static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
  130. {
  131. var u = 1 - t;
  132. var tt = t * t;
  133. var uu = u * u;
  134. var uuu = uu * u;
  135. var ttt = tt * t;
  136. var p = uuu * p0;
  137. p += 3 * uu * t * p1;
  138. p += 3 * u * tt * p2;
  139. p += ttt * p3;
  140. return p;
  141. }
  142. void AnimateCurve()
  143. {
  144. var newGrad = new Gradient();
  145. var colorKeys = new GradientColorKey[1];
  146. var alphaKeys = new GradientAlphaKey[2];
  147. var colorKey = new GradientColorKey(m_GradientKeyColor, 0f);
  148. colorKeys[0] = colorKey;
  149. var alphaKeyStart = new GradientAlphaKey(.25f, m_Time);
  150. var alphaKeyEnd = new GradientAlphaKey(1f, 1f);
  151. alphaKeys[0] = alphaKeyStart;
  152. alphaKeys[1] = alphaKeyEnd;
  153. newGrad.SetKeys(colorKeys, alphaKeys);
  154. newGrad.mode = GradientMode.Blend;
  155. m_LineRenderer.colorGradient = newGrad;
  156. m_Time += (Time.unscaledDeltaTime * m_AnimSpeed);
  157. if (m_Time >= 1f)
  158. m_Time = 0f;
  159. }
  160. }
  161. }