WuhuIslandTesting/Library/PackageCache/com.unity.splines@1.0.1/Editor/Controls/SplineDataHandlesDrawer.cs
2025-01-07 02:06:59 +01:00

260 lines
9.1 KiB
C#

using System;
using System.Reflection;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
static class SplineDataHandlesDrawer
{
/// <summary>
/// LabelType used to define if label must be displayed along the handle and how it should be formatted
/// </summary>
internal enum LabelType
{
None,
Index,
Time
};
const float k_HandleSize = 0.15f;
const int k_PickRes = 2;
internal static void InitCustomHandles<T>(
SplineData<T> splineData,
ISplineDataHandle drawerInstance)
{
var ids = new int[splineData.Count];
for(int kfIndex = 0; kfIndex < splineData.Count; kfIndex++)
ids[kfIndex] = GUIUtility.GetControlID(FocusType.Passive);
( (SplineDataHandle<T>)drawerInstance ).m_ControlIDs = ids;
}
internal static void DrawCustomHandles<T>(
SplineData<T> splineData,
Component component,
Spline spline,
Matrix4x4 localToWorld,
Color color,
ISplineDataHandle drawerInstance,
MethodInfo splineDataDrawMethodInfo,
MethodInfo keyframeDrawMethodInfo)
{
if(splineData.Count == 0)
return;
Undo.RecordObject(component, "Modifying Spline Data Points");
//Invoke if existing the custom drawer for the whole spline
splineDataDrawMethodInfo?.Invoke(drawerInstance,
new object[]
{
splineData,
spline,
localToWorld,
color
});
if (keyframeDrawMethodInfo == null)
return;
var native = new NativeSpline(spline, localToWorld);
var ids = ( (SplineDataHandle<T>)drawerInstance ).controlIDs;
for(int splineDataIndex = 0; splineDataIndex < splineData.Count; splineDataIndex++)
{
var keyframe = splineData[splineDataIndex];
var normalizedT = SplineUtility.GetNormalizedInterpolation(native, keyframe.Index, splineData.PathIndexUnit);
native.Evaluate(normalizedT, out float3 dataPosition, out float3 dataDirection, out float3 dataUp);
using(new Handles.DrawingScope(color))
{
//Invoke if existing the custom drawer for each keyframe
keyframeDrawMethodInfo?.Invoke(drawerInstance,
new object[]
{
ids[splineDataIndex],
(Vector3)dataPosition,
(Vector3)dataDirection,
(Vector3)dataUp,
splineData,
splineDataIndex
});
}
}
native.Dispose();
}
internal static void DrawSplineDataHandles<T>(
SplineData<T> splineData,
Component component,
Spline spline,
Matrix4x4 localToWorld,
Color color,
LabelType labelType)
{
Undo.RecordObject(component, "Modifying Spline Data Points");
using var native = new NativeSpline(spline, localToWorld);
using(new Handles.DrawingScope(color))
{
for(int keyframeIndex = 0; keyframeIndex < splineData.Count; keyframeIndex++)
{
var keyframe = splineData[keyframeIndex];
var inUse = SplineDataHandle(splineData, keyframe, native, labelType, keyframeIndex, out float time);
if(inUse)
{
keyframe.Index = time;
splineData.SetDataPointNoSort(keyframeIndex, keyframe);
//OnMouseUp event
if(GUIUtility.hotControl == 0)
splineData.SortIfNecessary();
}
}
}
}
internal static bool SplineDataHandle<T>(
SplineData<T> splineData,
IDataPoint dataPoint,
NativeSpline spline,
LabelType labelType,
int keyframeIndex,
out float newTime)
{
int id = GUIUtility.GetControlID(FocusType.Passive);
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(id);
var normalizedT = SplineUtility.GetNormalizedInterpolation(spline, dataPoint.Index, splineData.PathIndexUnit);
var dataPosition = SplineUtility.EvaluatePosition(spline, normalizedT);
switch (eventType)
{
case EventType.Layout:
{
if(!Tools.viewToolActive)
{
var dist = HandleUtility.DistanceToCircle(dataPosition, k_HandleSize * HandleUtility.GetHandleSize(dataPosition));
HandleUtility.AddControl(id, dist);
}
break;
}
case EventType.Repaint:
DrawSplineDataHandle(dataPosition, id);
DrawSplineDataLabel(dataPosition, labelType, dataPoint, keyframeIndex);
break;
case EventType.MouseDown:
if (evt.button == 0
&& HandleUtility.nearestControl == id
&& GUIUtility.hotControl == 0)
{
GUIUtility.hotControl = id;
evt.Use();
newTime = GetClosestSplineDataT(spline, splineData);
return true;
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
evt.Use();
newTime = GetClosestSplineDataT(spline, splineData);
return true;
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
evt.Use();
if(evt.button == 0)
{
GUIUtility.hotControl = 0;
newTime = GetClosestSplineDataT(spline, splineData);
return true;
}
}
break;
case EventType.MouseMove:
HandleUtility.Repaint();
break;
}
newTime = dataPoint.Index;
return false;
}
static void DrawSplineDataHandle(Vector3 position, int controlID)
{
var handleColor = Handles.color;
if(controlID == GUIUtility.hotControl)
handleColor = Handles.selectedColor;
else if(GUIUtility.hotControl == 0 && controlID == HandleUtility.nearestControl)
handleColor = Handles.preselectionColor;
// to avoid affecting the sphere dimensions with the handles matrix, we'll just use the position and reset
// the matrix to identity when drawing.
position = Handles.matrix * position;
using(new Handles.DrawingScope(handleColor, Matrix4x4.identity))
{
Handles.SphereHandleCap(
controlID,
position,
Quaternion.identity,
k_HandleSize * HandleUtility.GetHandleSize(position),
EventType.Repaint
);
}
}
static void DrawSplineDataLabel(Vector3 position, LabelType labelType, IDataPoint dataPoint, int keyframeIndex)
{
if(labelType == LabelType.None)
return;
float labelVal = dataPoint.Index;
if(labelType == LabelType.Index && keyframeIndex >= 0)
labelVal = keyframeIndex;
var label = ( Mathf.RoundToInt(labelVal * 100) / 100f ).ToString();
label = labelType == LabelType.Index ? "[" + label + "]" : "t: "+label;
Handles.Label(position - 0.1f * Vector3.up, label);
}
// Spline must be in world space
static float GetClosestSplineDataT<T>(NativeSpline spline, SplineData<T> splineData)
{
var evt = Event.current;
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
SplineUtility.GetNearestPoint(spline,
ray,
out float3 _,
out float t,
k_PickRes);
var time = SplineUtility.ConvertIndexUnit(
spline,
t,
PathIndexUnit.Normalized,
splineData.PathIndexUnit);
return time;
}
}
}