203 lines
7.2 KiB
C#
203 lines
7.2 KiB
C#
|
using System;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace Unity.VRTemplate
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Draws a bezier curve from a starting point transform to an end point transform
|
||
|
/// </summary>
|
||
|
public class BezierCurve : MonoBehaviour
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// If the view scale changes more than this amount, then the line width will be updated causing the line to be rebuilt.
|
||
|
/// </summary>
|
||
|
const float k_ViewerScaleChangeThreshold = 0.1f;
|
||
|
|
||
|
/// <summary>
|
||
|
/// The time within the frame that the curve will be updated.
|
||
|
/// </summary>
|
||
|
/// <seealso cref="UnityEngine.XR.Interaction.Toolkit.XRBaseController.UpdateType"/>
|
||
|
public enum UpdateType
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// 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.
|
||
|
/// </summary>
|
||
|
UpdateAndBeforeRender,
|
||
|
|
||
|
/// <summary>
|
||
|
/// Only sample input during the update phase of the frame.
|
||
|
/// </summary>
|
||
|
Update,
|
||
|
|
||
|
/// <summary>
|
||
|
/// Only sample input directly before rendering.
|
||
|
/// </summary>
|
||
|
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<IProvidesViewerScale>.provider { get; set; }
|
||
|
|
||
|
void Awake()
|
||
|
{
|
||
|
if (m_LineRenderer == null)
|
||
|
m_LineRenderer = GetComponent<LineRenderer>();
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Updates the line points to draw the bezier curve.
|
||
|
/// </summary>
|
||
|
[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;
|
||
|
}
|
||
|
}
|
||
|
}
|