// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Animation.Interpolation;
using UltimateXR.Core;
using UltimateXR.Extensions.Unity.Render;
using UnityEditor;
using UnityEngine;
namespace UltimateXR.Editor.Animation.Interpolation
{
///
/// Custom inspector drawer for .
///
[CustomPropertyDrawer(typeof(UxrEasing))]
public class UxrEasingDrawer : PropertyDrawer
{
#region Public Types & Data
///
/// This constant determines the graph height in pixels.
///
public const int GraphHeight = 80;
#endregion
#region Constructors & Finalizer
///
/// Creates the temporal material to draw the graph.
///
public UxrEasingDrawer()
{
var shader = Shader.Find(UxrConstants.Shaders.HiddenInternalColoredShader);
_lineMaterial = new Material(shader);
}
///
/// Destroys the temporal material to draw the graph.
///
~UxrEasingDrawer()
{
Object.DestroyImmediate(_lineMaterial);
}
#endregion
#region Public Overrides PropertyDrawer
///
/// Gets the height in pixels required to draw the property.
///
/// Serialized property describing an
/// UI label
/// Height in pixels
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.singleLineHeight + GraphHeight;
}
#endregion
#region Public Methods
///
/// Draws the easing graph.
///
/// Target rect
/// Material used
/// The line color
/// The easing used
/// The loop mode
/// The number of loops to draw
public static void DrawGraph(Rect rect, Material material, Color color, UxrEasing easing, UxrLoopMode loopMode = UxrLoopMode.None, int loops = 1)
{
// Make coordinates relative to the rect.
GUI.BeginClip(rect);
// Enable the internal material.
material.SetPass(0);
// Draw background. Use alpha to avoid getting too dark.
GL.Begin(GL.QUADS);
GL.Color(Color.black.WithAlpha(0.4f));
GL.Vertex3(0, rect.height, 0);
GL.Vertex3(rect.width, rect.height, 0);
GL.Vertex3(rect.width, 0, 0);
GL.Vertex3(0, 0, 0);
GL.End();
// Now draw the graph as a connected set of points.
GL.Begin(GL.LINE_STRIP);
GL.Color(color);
// Get the min/max graph values.
// This is important because some interpolation curves go out the [0, 1] range.
GetGraphRange(easing, out float min, out float max);
// Iterate over points and draw vertices.
for (int i = 0; i < CurveSegments + 1; ++i)
{
float t = (float)i / CurveSegments;
float value = UxrInterpolator.Interpolate(Vector4.one, Vector4.zero, 1.0f, 0.0f, t * loops, easing, loopMode).x;
float valueScaled = Mathf.InverseLerp(min, max, value);
GL.Vertex3(t * rect.width, rect.height * valueScaled, 0);
}
GL.End();
GUI.EndClip();
}
#endregion
#region Unity
///
/// Draws the inspector and handles input.
///
/// Position where to draw the inspector
/// Serialized property to draw
/// UI label
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// Draw the property label and value
EditorGUI.PropertyField(UxrEditorUtils.GetRect(position, 0), property, label);
// Get the graph rect. Overwrite with graph height in pixels and indentation so that it is drawn below the values only and not taking the whole inspector width.
Rect rect = UxrEditorUtils.GetRect(position, 1);
rect.height = GraphHeight;
rect.xMin += EditorGUIUtility.labelWidth;
// Get our easing value from the property.
UxrEasing easing = (UxrEasing)property.enumValueIndex;
// Draw the graph!
if (Event.current.type == EventType.Repaint)
{
DrawGraph(rect, _lineMaterial, Color.green, easing);
}
}
#endregion
#region Private Methods
///
/// Gets the min and max values for a type of interpolation.
///
/// Easing
/// Returns the min graph value
/// Returns the max graph value
private static void GetGraphRange(UxrEasing easing, out float min, out float max)
{
min = float.MaxValue;
max = float.MinValue;
for (int i = 0; i < CurveSegments + 1; ++i)
{
float t = (float)i / CurveSegments;
float value = UxrInterpolator.Interpolate(1.0f, 0.0f, t, easing);
if (value < min)
{
min = value;
}
if (value > max)
{
max = value;
}
}
}
#endregion
#region Private Types & Data
///
/// Determines the amount of segments to draw the graph with.
///
private const int CurveSegments = 200;
private readonly Material _lineMaterial;
#endregion
}
}