using System;
using UnityEngine;
namespace Unity.VRTemplate
{
///
/// Draws a bezier curve from a starting point transform to an end point transform
///
public class BezierCurve : MonoBehaviour
{
///
/// If the view scale changes more than this amount, then the line width will be updated causing the line to be rebuilt.
///
const float k_ViewerScaleChangeThreshold = 0.1f;
///
/// The time within the frame that the curve will be updated.
///
///
public enum UpdateType
{
///
/// Sample at both update and directly before rendering. For smooth tracking,
/// we recommend using this value as it will provide the lowest input latency for the device.
///
UpdateAndBeforeRender,
///
/// Only sample input during the update phase of the frame.
///
Update,
///
/// Only sample input directly before rendering.
///
BeforeRender,
}
#pragma warning disable 649
[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.")]
UpdateType m_UpdateTrackingType = UpdateType.Update;
[SerializeField, Tooltip("The transform that determines the position, handle rotation, and handle scale of the start point of the bezier curve.")]
Transform m_StartPoint;
[SerializeField, Tooltip("The transform that determines the position, handle rotation, and handle scale of the end point of the bezier curve.")]
Transform m_EndPoint;
[SerializeField, Tooltip("Controls the scale factor of the curve's start bezier handle.")]
float m_CurveFactorStart = 1.0f;
[SerializeField, Tooltip("Controls the scale factor of the curve's end bezier handle.")]
float m_CurveFactorEnd = 1.0f;
[SerializeField, Tooltip("Controls the number of segments used to draw the curve.")]
int m_SegmentCount = 50;
[SerializeField, Tooltip("When enabled, the line color gradient will be animated so that an opaque part travels along the line.")]
bool m_Animate;
[SerializeField, Tooltip("If animated, this controls the speed that the animation of the line.")]
float m_AnimSpeed = 0.25f;
[SerializeField, Tooltip("If animated, this color will be the main opaque color of the gradient")]
Color m_GradientKeyColor = new Color(0.1254902f, 0.5882353f, 0.9529412f);
[SerializeField, Tooltip("The line renderer that will draw the curve. If not set it will find a line renderer on this GameObject.")]
LineRenderer m_LineRenderer;
#pragma warning restore 649
Vector3[] m_ControlPoints = new Vector3[4];
float m_Time;
float m_LineWidth;
float m_LastViewerScale;
Vector3 m_LastStartPosition;
Vector3 m_LastEndPosition;
//IProvidesViewerScale IFunctionalitySubscriber.provider { get; set; }
void Awake()
{
if (m_LineRenderer == null)
m_LineRenderer = GetComponent();
m_LineWidth = m_LineRenderer.startWidth;
}
void OnEnable()
{
DrawCurve();
Application.onBeforeRender += OnBeforeRender;
}
void OnDisable()
{
Application.onBeforeRender -= OnBeforeRender;
}
void OnBeforeRender()
{
if (m_UpdateTrackingType == UpdateType.BeforeRender || m_UpdateTrackingType == UpdateType.UpdateAndBeforeRender)
DrawCurve();
}
void Update()
{
if (m_UpdateTrackingType == UpdateType.Update || m_UpdateTrackingType == UpdateType.UpdateAndBeforeRender)
DrawCurve();
if (m_Animate)
{
AnimateCurve();
}
}
///
/// Updates the line points to draw the bezier curve.
///
[ContextMenu("Draw")]
public void DrawCurve()
{
var startPointPosition = m_StartPoint.position;
var endPointPosition = m_EndPoint.position;
if (startPointPosition == m_LastStartPosition &&
endPointPosition == m_LastEndPosition)
return; // Return early if the start and end have not changed to avoid recalculating the curve
var dist = Vector3.Distance(startPointPosition, endPointPosition);
m_ControlPoints[0] = startPointPosition;
m_ControlPoints[1] = startPointPosition + (m_StartPoint.right * (dist * m_CurveFactorStart));
m_ControlPoints[2] = endPointPosition - (m_EndPoint.right * (dist * m_CurveFactorEnd));
m_ControlPoints[3] = endPointPosition;
int segmentCount;
const float smallestCurveLength = 0.0125f;
if (Vector3.Distance(startPointPosition, endPointPosition) < (smallestCurveLength * m_LastViewerScale))
{
segmentCount = 2;
}
else
{
segmentCount = m_SegmentCount;
}
m_LineRenderer.positionCount = segmentCount + 1;
m_LineRenderer.SetPosition(0, m_ControlPoints[0]);
for (var i = 1; i <= segmentCount; i++)
{
var t = i / (float)segmentCount;
var pixel = CalculateCubicBezierPoint(t, m_ControlPoints[0], m_ControlPoints[1], m_ControlPoints[2], m_ControlPoints[3]);
m_LineRenderer.SetPosition(i, pixel);
}
m_LastStartPosition = startPointPosition;
m_LastEndPosition = endPointPosition;
}
static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
var u = 1 - t;
var tt = t * t;
var uu = u * u;
var uuu = uu * u;
var ttt = tt * t;
var p = uuu * p0;
p += 3 * uu * t * p1;
p += 3 * u * tt * p2;
p += ttt * p3;
return p;
}
void AnimateCurve()
{
var newGrad = new Gradient();
var colorKeys = new GradientColorKey[1];
var alphaKeys = new GradientAlphaKey[2];
var colorKey = new GradientColorKey(m_GradientKeyColor, 0f);
colorKeys[0] = colorKey;
var alphaKeyStart = new GradientAlphaKey(.25f, m_Time);
var alphaKeyEnd = new GradientAlphaKey(1f, 1f);
alphaKeys[0] = alphaKeyStart;
alphaKeys[1] = alphaKeyEnd;
newGrad.SetKeys(colorKeys, alphaKeys);
newGrad.mode = GradientMode.Blend;
m_LineRenderer.colorGradient = newGrad;
m_Time += (Time.unscaledDeltaTime * m_AnimSpeed);
if (m_Time >= 1f)
m_Time = 0f;
}
}
}