initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class PathGUIControls
|
||||
{
|
||||
internal static readonly List<Vector3> pointsBuffer = new List<Vector3>();
|
||||
|
||||
public static void MultiEditVector3Field(GUIContent label, IList<Vector3> points)
|
||||
{
|
||||
if (!EditorGUIUtility.wideMode)
|
||||
GUILayout.Label(label);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (EditorGUIUtility.wideMode)
|
||||
EditorGUILayout.PrefixLabel(label);
|
||||
|
||||
var prevMixedValue = EditorGUI.showMixedValue;
|
||||
var result = GetMultiEditValue(points, out Vector3 value);
|
||||
|
||||
EditorGUI.showMixedValue = result.xMixed;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUILayout.Label("X");
|
||||
float x = EditorGUILayout.FloatField(value.x);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
for (int i = 0; i < points.Count; ++i)
|
||||
{
|
||||
var oldValue = points[i];
|
||||
oldValue.x = x;
|
||||
points[i] = oldValue;
|
||||
}
|
||||
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = result.yMixed;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUILayout.Label("Y");
|
||||
float y = EditorGUILayout.FloatField(value.y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
for (int i = 0; i < points.Count; ++i)
|
||||
{
|
||||
var oldValue = points[i];
|
||||
oldValue.y = y;
|
||||
points[i] = oldValue;
|
||||
}
|
||||
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = result.zMixed;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUILayout.Label("Z");
|
||||
float z = EditorGUILayout.FloatField(value.z);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
for (int i = 0; i < points.Count; ++i)
|
||||
{
|
||||
var oldValue = points[i];
|
||||
oldValue.z = z;
|
||||
points[i] = oldValue;
|
||||
}
|
||||
|
||||
GUI.changed = true;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.showMixedValue = prevMixedValue;
|
||||
}
|
||||
|
||||
//Returns true if the value has multiple values
|
||||
static (bool xMixed, bool yMixed, bool zMixed) GetMultiEditValue(IList<Vector3> points, out Vector3 value)
|
||||
{
|
||||
value = points[0];
|
||||
bool xMixed = false;
|
||||
bool yMixed = false;
|
||||
bool zMixed = false;
|
||||
for (int i = 1; i < points.Count; ++i)
|
||||
{
|
||||
Vector3 point = points[i];
|
||||
xMixed |= !Mathf.Approximately(value.x, point.x);
|
||||
yMixed |= !Mathf.Approximately(value.y, point.y);
|
||||
zMixed |= !Mathf.Approximately(value.z, point.z);
|
||||
|
||||
if (xMixed && yMixed && zMixed)
|
||||
break;
|
||||
}
|
||||
|
||||
return (xMixed, yMixed, zMixed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the mixed value for a field
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The target type</typeparam>
|
||||
/// <param name="values">A list of all the values</param>
|
||||
/// <param name="value">The value that should be used for the field</param>
|
||||
/// <returns>True if has mixed values</returns>
|
||||
public static bool GetMixedValue<T>(IList<T> values, out T value)
|
||||
where T : IEquatable<T>
|
||||
{
|
||||
value = values[0];
|
||||
for (int i = 1; i < values.Count; ++i)
|
||||
{
|
||||
if (!value.Equals(values[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d5e51246e7862284486a935085c13937
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,260 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 54bce7290a9e479889adc3e8c822798e
|
||||
timeCreated: 1625512898
|
|
@ -0,0 +1,216 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
class SplineElementRectSelector
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
None,
|
||||
Replace,
|
||||
Add,
|
||||
Subtract
|
||||
}
|
||||
|
||||
static class Styles
|
||||
{
|
||||
public static readonly GUIStyle selectionRect = GUI.skin.FindStyle("selectionRect");
|
||||
}
|
||||
|
||||
Rect m_Rect;
|
||||
Vector2 m_StartPos;
|
||||
Mode m_Mode;
|
||||
Mode m_InitialMode;
|
||||
static readonly HashSet<ISplineElement> s_SplineElementsCompareSet = new HashSet<ISplineElement>();
|
||||
static readonly List<ISplineElement> s_SplineElementsBuffer = new List<ISplineElement>();
|
||||
static readonly HashSet<ISplineElement> s_PreRectSelectionElements = new HashSet<ISplineElement>();
|
||||
|
||||
public void OnGUI(IReadOnlyList<IEditableSpline> paths)
|
||||
{
|
||||
int id = GUIUtility.GetControlID(FocusType.Passive);
|
||||
Event evt = Event.current;
|
||||
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.Layout:
|
||||
if (!Tools.viewToolActive)
|
||||
{
|
||||
HandleUtility.AddDefaultControl(id);
|
||||
|
||||
if (m_Mode != Mode.None)
|
||||
{
|
||||
// If we've started rect select in Add or Subtract modes, then if we were in a Replace
|
||||
// mode just before (i.e. the shift or action has been released temporarily),
|
||||
// we need to bring back the pre rect selection elements into current selection.
|
||||
if (m_InitialMode != Mode.Replace && RefreshSelectionMode())
|
||||
{
|
||||
SplineSelection.Clear();
|
||||
s_SplineElementsCompareSet.Clear();
|
||||
|
||||
if (m_Mode != Mode.Replace)
|
||||
{
|
||||
foreach (var element in s_PreRectSelectionElements)
|
||||
SplineSelection.Add(element);
|
||||
}
|
||||
|
||||
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
|
||||
UpdateSelection(m_Rect, paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EventType.Repaint:
|
||||
if (GUIUtility.hotControl == id && m_Rect.size != Vector2.zero)
|
||||
{
|
||||
Handles.BeginGUI();
|
||||
Styles.selectionRect.Draw(m_Rect, GUIContent.none, false, false, false, false);
|
||||
Handles.EndGUI();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
if (HandleUtility.nearestControl == id && evt.button == 0)
|
||||
{
|
||||
m_StartPos = evt.mousePosition;
|
||||
m_Rect = new Rect(Vector3.zero, Vector2.zero);
|
||||
|
||||
BeginSelection(paths);
|
||||
GUIUtility.hotControl = id;
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
|
||||
evt.Use();
|
||||
|
||||
UpdateSelection(m_Rect, paths);
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
|
||||
EndSelection(m_Rect, paths);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void BeginSelection(IReadOnlyList<IEditableSpline> paths)
|
||||
{
|
||||
RefreshSelectionMode();
|
||||
m_InitialMode = m_Mode;
|
||||
|
||||
s_SplineElementsCompareSet.Clear();
|
||||
s_SplineElementsBuffer.Clear();
|
||||
if (m_Mode == Mode.Replace)
|
||||
{
|
||||
SplineSelection.Clear();
|
||||
s_PreRectSelectionElements.Clear();
|
||||
}
|
||||
else
|
||||
SplineSelection.GetSelectedElements(s_PreRectSelectionElements);
|
||||
}
|
||||
|
||||
protected virtual void UpdateSelection(Rect rect, IReadOnlyList<IEditableSpline> paths)
|
||||
{
|
||||
//Get all elements in rect
|
||||
s_SplineElementsBuffer.Clear();
|
||||
for (int i = 0; i < paths.Count; ++i)
|
||||
{
|
||||
IEditableSpline spline = paths[i];
|
||||
for (int j = 0; j < spline.knotCount; ++j)
|
||||
GetElementSelection(rect, spline, j, s_SplineElementsBuffer);
|
||||
}
|
||||
|
||||
foreach (var splineElement in s_SplineElementsBuffer)
|
||||
{
|
||||
//Compare current frame buffer with last frame's to find new additions/removals
|
||||
var wasInRectLastFrame = s_SplineElementsCompareSet.Remove(splineElement);
|
||||
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
|
||||
{
|
||||
var canAdd = m_Mode == Mode.Replace ? true : !s_PreRectSelectionElements.Contains(splineElement);
|
||||
if (!wasInRectLastFrame && canAdd)
|
||||
SplineSelection.Add(splineElement);
|
||||
}
|
||||
else if (m_Mode == Mode.Subtract && !wasInRectLastFrame)
|
||||
{
|
||||
SplineSelection.Remove(splineElement);
|
||||
}
|
||||
}
|
||||
|
||||
//Remaining spline elements from last frame are removed from selection (or added if mode is subtract)
|
||||
foreach (var splineElement in s_SplineElementsCompareSet)
|
||||
{
|
||||
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
|
||||
{
|
||||
// If we're in Add mode, don't remove elements that were in select prior to rect selection
|
||||
if (m_Mode == Mode.Add && s_PreRectSelectionElements.Contains(splineElement))
|
||||
continue;
|
||||
SplineSelection.Remove(splineElement);
|
||||
}
|
||||
else if (m_Mode == Mode.Subtract && s_PreRectSelectionElements.Contains(splineElement))
|
||||
SplineSelection.Add(splineElement);
|
||||
}
|
||||
|
||||
//Move current elements buffer to hash set for next frame compare
|
||||
s_SplineElementsCompareSet.Clear();
|
||||
foreach (var splineElement in s_SplineElementsBuffer)
|
||||
s_SplineElementsCompareSet.Add(splineElement);
|
||||
}
|
||||
|
||||
bool RefreshSelectionMode()
|
||||
{
|
||||
var modeBefore = m_Mode;
|
||||
if (Event.current.shift)
|
||||
m_Mode = Mode.Add;
|
||||
else if (EditorGUI.actionKey)
|
||||
m_Mode = Mode.Subtract;
|
||||
else
|
||||
m_Mode = Mode.Replace;
|
||||
|
||||
// Return true if the mode has changed
|
||||
return m_Mode != modeBefore;
|
||||
}
|
||||
|
||||
void GetElementSelection(Rect rect, IEditableSpline spline, int index, List<ISplineElement> results)
|
||||
{
|
||||
var knot = spline.GetKnot(index);
|
||||
Vector3 screenSpace = HandleUtility.WorldToGUIPointWithDepth(knot.position);
|
||||
|
||||
if (screenSpace.z > 0 && rect.Contains(screenSpace))
|
||||
results.Add(knot);
|
||||
|
||||
for(int tangentIndex = 0; tangentIndex < knot.tangentCount; tangentIndex++)
|
||||
{
|
||||
var tangent = knot.GetTangent(tangentIndex);
|
||||
screenSpace = HandleUtility.WorldToGUIPointWithDepth(tangent.position);
|
||||
|
||||
if (SplineSelectionUtility.IsSelectable(spline, index, tangent) && screenSpace.z > 0 && rect.Contains(screenSpace))
|
||||
results.Add(tangent);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void EndSelection(Rect rect, IReadOnlyList<IEditableSpline> paths)
|
||||
{
|
||||
m_Mode = m_InitialMode = Mode.None;
|
||||
}
|
||||
|
||||
static Rect GetRectFromPoints(Vector2 a, Vector2 b)
|
||||
{
|
||||
Vector2 min = new Vector2(Mathf.Min(a.x, b.x), Mathf.Min(a.y, b.y));
|
||||
Vector2 max = new Vector2(Mathf.Max(a.x, b.x), Mathf.Max(a.y, b.y));
|
||||
|
||||
return new Rect(min, max - min);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3d68b08563e248f4a02b78fb710d36bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,613 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.SettingsManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Splines;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class SplineHandles
|
||||
{
|
||||
[UserSetting]
|
||||
internal static UserSetting<Color> s_LineNormalFrontColor = new UserSetting<Color>(PathSettings.instance, "Handles.CurveNormalInFrontColor", Color.white, SettingsScope.User);
|
||||
|
||||
[UserSetting]
|
||||
internal static UserSetting<Color> s_LineNormalBehindColor = new UserSetting<Color>(PathSettings.instance, "Handles.CurveNormalBehindColor", new Color(0.98f, 0.62f, 0.62f, 0.4f), SettingsScope.User);
|
||||
|
||||
[UserSetting]
|
||||
internal static UserSetting<Color> s_KnotColor = new UserSetting<Color>(PathSettings.instance, "Handles.KnotDefaultColor", new Color(.4f, 1f, .95f, 1f), SettingsScope.User);
|
||||
|
||||
[UserSetting]
|
||||
internal static UserSetting<Color> s_TangentColor = new UserSetting<Color>(PathSettings.instance, "Handles.TangentDefaultColor", Color.black, SettingsScope.User);
|
||||
|
||||
[UserSettingBlock("Handles")]
|
||||
static void HandleColorPreferences(string searchContext)
|
||||
{
|
||||
s_LineNormalFrontColor.value = SettingsGUILayout.SettingsColorField("Curve Color", s_LineNormalFrontColor, searchContext);
|
||||
s_LineNormalBehindColor.value = SettingsGUILayout.SettingsColorField("Curve Color Behind Surface", s_LineNormalBehindColor, searchContext);
|
||||
s_KnotColor.value = SettingsGUILayout.SettingsColorField("Knot Color", s_KnotColor, searchContext);
|
||||
s_TangentColor.value = SettingsGUILayout.SettingsColorField("Tangent Color", s_TangentColor, searchContext);
|
||||
}
|
||||
|
||||
const float k_SizeFactor = 0.15f;
|
||||
const float k_PickingDistance = 8f;
|
||||
|
||||
const float k_HandleWidthDefault = 2f;
|
||||
const float k_HandleWidthHover = 4f;
|
||||
|
||||
const float k_KnotDiscRadiusFactorDefault = 0.06f;
|
||||
const float k_KnotDiscRadiusFactorHover = 0.07f;
|
||||
const float k_KnotDiscRadiusFactorSelected = 0.085f;
|
||||
|
||||
const float k_KnotRotDiscRadius = 0.18f;
|
||||
const float k_KnotRotDiscWidthDefault = 1.5f;
|
||||
const float k_KnotRotDiscWidthHover = 3f;
|
||||
const float k_KnotRotDiscWidthSelected = 4f;
|
||||
|
||||
const float k_TangentLineWidthDefault = 2f;
|
||||
const float k_TangentLineWidthHover = 3.5f;
|
||||
const float k_TangentLineWidthSelected = 4.5f;
|
||||
const float k_TangentStartOffsetFromKnot = 0.22f;
|
||||
const float k_tangentEndOffsetFromHandle = 0.11f;
|
||||
|
||||
const float k_AliasedLineSizeMultiplier = 0.5f;
|
||||
|
||||
const int k_SegmentCount = 30;
|
||||
const float k_CurveLineWidth = 5f;
|
||||
const float k_PreviewCurveOpacity = 0.5f;
|
||||
const string k_TangentLineAATexPath = "Textures/TangentLineAATex";
|
||||
|
||||
static Texture2D s_ThickTangentLineAATex = Resources.Load<Texture2D>(k_TangentLineAATexPath);
|
||||
|
||||
static readonly Vector3[] s_CurveSegmentsBuffer = new Vector3[k_SegmentCount + 1];
|
||||
static readonly Vector3[] s_SegmentBuffer = new Vector3[2];
|
||||
static readonly Vector3[] s_AAWireDiscBuffer = new Vector3[18];
|
||||
|
||||
static ISplineElement s_LastHoveredTangent;
|
||||
static int s_LastHoveredTangentID;
|
||||
static List<int> s_ElementChildIDs = new List<int>();
|
||||
|
||||
internal static void DrawSplineHandles(IReadOnlyList<IEditableSpline> paths, SplineHandlesOptions options)
|
||||
{
|
||||
for (int i = 0; i < paths.Count; ++i)
|
||||
{
|
||||
DrawSplineHandles(paths[i], options);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool DrawSplineHandles(IEditableSpline spline, SplineHandlesOptions options, bool activeSpline = true)
|
||||
{
|
||||
int lastIndex = spline.closed ? spline.knotCount - 1 : spline.knotCount - 2; //If the spline isn't closed, skip the last index of the spline
|
||||
var isInsertingKnots = HasOption(options, SplineHandlesOptions.KnotInsert);
|
||||
|
||||
int[] curveIDs = new int[0];
|
||||
if(isInsertingKnots && lastIndex+1>=0)
|
||||
{
|
||||
curveIDs = new int[lastIndex+1];
|
||||
for (int idIndex = 0; idIndex < lastIndex+1; ++idIndex)
|
||||
curveIDs[idIndex] = GUIUtility.GetControlID(FocusType.Passive);
|
||||
}
|
||||
|
||||
activeSpline = curveIDs.Contains(HandleUtility.nearestControl) || activeSpline;
|
||||
for (int knotIndex = 0; knotIndex <= lastIndex; ++knotIndex)
|
||||
{
|
||||
var curve = new CurveData(spline, knotIndex);
|
||||
if (isInsertingKnots)
|
||||
CurveHandleWithKnotInsert(curve, curveIDs[knotIndex], activeSpline);
|
||||
else
|
||||
DrawCurve(curve);
|
||||
}
|
||||
|
||||
var drawHandlesAsActive = curveIDs.Contains(HandleUtility.nearestControl) || activeSpline;
|
||||
|
||||
for (int knotIndex = 0; knotIndex < spline.knotCount; ++knotIndex)
|
||||
{
|
||||
var knot = spline.GetKnot(knotIndex);
|
||||
|
||||
if (HasOption(options, SplineHandlesOptions.ShowTangents))
|
||||
{
|
||||
for (int tangentIndex = 0; tangentIndex < knot.tangentCount; ++tangentIndex)
|
||||
{
|
||||
//Not drawing unused tangents
|
||||
if (!spline.closed && ((knotIndex == 0 && tangentIndex == 0) ||
|
||||
(knotIndex != 0 && knotIndex + 1 == spline.knotCount && tangentIndex + 1 == knot.tangentCount)))
|
||||
continue;
|
||||
|
||||
var tangent = knot.GetTangent(tangentIndex);
|
||||
if (HasOption(options, SplineHandlesOptions.SelectableTangents))
|
||||
{
|
||||
var tangentHandlelID = SelectionHandle(tangent);
|
||||
s_ElementChildIDs.Add(tangentHandlelID);
|
||||
}
|
||||
else
|
||||
DrawTangentHandle(tangent, -1, drawHandlesAsActive);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasOption(options, SplineHandlesOptions.SelectableKnots))
|
||||
SelectionHandle(knot);
|
||||
else
|
||||
DrawKnotHandle(knot, null, drawHandlesAsActive);
|
||||
|
||||
s_ElementChildIDs.Clear();
|
||||
}
|
||||
|
||||
if (s_LastHoveredTangent != null && Event.current.GetTypeForControl(s_LastHoveredTangentID) == EventType.Repaint)
|
||||
s_LastHoveredTangent = null;
|
||||
|
||||
return activeSpline;
|
||||
}
|
||||
|
||||
static bool HasOption(SplineHandlesOptions options, SplineHandlesOptions target)
|
||||
{
|
||||
return (options & target) == target;
|
||||
}
|
||||
|
||||
internal static int SelectionHandle(ISplineElement element)
|
||||
{
|
||||
int id = GUIUtility.GetControlID(FocusType.Passive);
|
||||
Event evt = Event.current;
|
||||
EventType eventType = evt.GetTypeForControl(id);
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.Layout:
|
||||
if (!Tools.viewToolActive)
|
||||
{
|
||||
HandleUtility.AddControl(id, SplineHandleUtility.DistanceToCircle(element.position, k_PickingDistance));
|
||||
|
||||
if (element is EditableTangent)
|
||||
{
|
||||
if (HandleUtility.nearestControl == id)
|
||||
{
|
||||
s_LastHoveredTangent = element;
|
||||
s_LastHoveredTangentID = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
if (HandleUtility.nearestControl == id)
|
||||
{
|
||||
//Clicking a knot selects it
|
||||
if (evt.button != 0)
|
||||
break;
|
||||
|
||||
GUIUtility.hotControl = id;
|
||||
evt.Use();
|
||||
|
||||
//Add/Remove from knotSelection
|
||||
if (EditorGUI.actionKey || evt.modifiers == EventModifiers.Shift)
|
||||
{
|
||||
if (SplineSelection.Contains(element))
|
||||
SplineSelection.Remove(element);
|
||||
else
|
||||
SplineSelection.Add(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
SplineSelection.Clear();
|
||||
SplineSelection.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EventType.MouseMove:
|
||||
if (id == HandleUtility.nearestControl)
|
||||
HandleUtility.Repaint();
|
||||
break;
|
||||
|
||||
case EventType.Repaint:
|
||||
switch (element)
|
||||
{
|
||||
case EditableKnot knot:
|
||||
DrawKnotHandle(knot, id, s_ElementChildIDs);
|
||||
break;
|
||||
case EditableTangent tangent:
|
||||
DrawTangentHandle(tangent, id);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
internal static bool ButtonHandle(int controlID, EditableKnot knot, bool active)
|
||||
{
|
||||
Event evt = Event.current;
|
||||
EventType eventType = evt.GetTypeForControl(controlID);
|
||||
|
||||
var position = knot.position;
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.Layout:
|
||||
{
|
||||
if(!Tools.viewToolActive)
|
||||
HandleUtility.AddControl(controlID, SplineHandleUtility.DistanceToKnot(position));
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType.Repaint:
|
||||
DrawKnotHandle(knot, controlID, null, active);
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
if (HandleUtility.nearestControl == controlID)
|
||||
{
|
||||
//Clicking a knot selects it
|
||||
if (evt.button != 0)
|
||||
break;
|
||||
|
||||
GUIUtility.hotControl = controlID;
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == controlID)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseMove:
|
||||
if (HandleUtility.nearestControl == controlID)
|
||||
HandleUtility.Repaint();
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void CurveHandleWithKnotInsert(CurveData curve, int controlID, bool activeSpline)
|
||||
{
|
||||
Event evt = Event.current;
|
||||
EventType eventType = evt.GetTypeForControl(controlID);
|
||||
|
||||
CurveHandleCap(curve, controlID, eventType, false, activeSpline);
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.Repaint:
|
||||
if (HandleUtility.nearestControl == controlID)
|
||||
{
|
||||
SplineHandleUtility.GetNearestPointOnCurve(curve, out Vector3 position, out float t);
|
||||
if(t > 0f && t < 1f)
|
||||
{
|
||||
var mouseRect = new Rect(evt.mousePosition - new Vector2(500, 500), new Vector2(1000, 1000));
|
||||
EditorGUIUtility.AddCursorRect(mouseRect, MouseCursor.ArrowPlus);
|
||||
|
||||
var previewKnotRotation = quaternion.identity;
|
||||
if (curve.a.spline is BezierEditableSpline)
|
||||
{
|
||||
var bezierCurve = BezierCurve.FromTangent(curve.a.position, curve.a.GetTangent((int)BezierTangent.Out).direction,
|
||||
curve.b.position, curve.b.GetTangent((int)BezierTangent.In).direction);
|
||||
|
||||
var up = CurveUtility.EvaluateUpVector(bezierCurve, t, math.rotate(curve.a.rotation, math.up()), math.rotate(curve.b.rotation, math.up()));
|
||||
var tangentOut = CurveUtility.EvaluateTangent(bezierCurve, t);
|
||||
previewKnotRotation = quaternion.LookRotationSafe(math.normalize(tangentOut), up);
|
||||
}
|
||||
|
||||
DrawKnotHandle(position, previewKnotRotation, false, controlID, false, activeSpline);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
if (HandleUtility.nearestControl == controlID)
|
||||
{
|
||||
if (evt.button != 0)
|
||||
break;
|
||||
|
||||
SplineHandleUtility.GetNearestPointOnCurve(curve, out Vector3 position, out float t);
|
||||
|
||||
//Do not place a new knot on an existing one to prevent creation of singularity points with bad tangents
|
||||
if(t > 0f && t < 1f)
|
||||
{
|
||||
EditableKnot knot = EditableSplineUtility.InsertKnotOnCurve(curve, position, t);
|
||||
|
||||
if(!(evt.control || evt.shift))
|
||||
SplineSelection.Clear();
|
||||
|
||||
SplineSelection.Add(knot);
|
||||
}
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseMove:
|
||||
if(HandleUtility.nearestControl == controlID)
|
||||
HandleUtility.Repaint();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DrawKnotHandle(EditableKnot knot, List<int> tangentControlIDs = null, bool activeSpline = true)
|
||||
{
|
||||
DrawKnotHandle(knot, -1, tangentControlIDs, activeSpline);
|
||||
}
|
||||
|
||||
internal static void DrawKnotHandle(EditableKnot knot, int controlId = -1, List<int> tangentControlIDs = null, bool activeSpline = true)
|
||||
{
|
||||
var mirroredTangentSelected = false;
|
||||
var mirroredTangentHovered = false;
|
||||
|
||||
if (tangentControlIDs != null && knot is BezierEditableKnot bezierKnot &&
|
||||
(bezierKnot.mode == BezierEditableKnot.Mode.Mirrored || bezierKnot.mode == BezierEditableKnot.Mode.Continuous))
|
||||
{
|
||||
for (int i = 0; i < knot.tangentCount; i++)
|
||||
{
|
||||
var tangent = knot.GetTangent(i);
|
||||
if (SplineSelection.Contains(tangent))
|
||||
{
|
||||
mirroredTangentSelected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mirroredTangentSelected)
|
||||
{
|
||||
foreach (var tangentID in tangentControlIDs)
|
||||
{
|
||||
if (HandleUtility.nearestControl == tangentID)
|
||||
mirroredTangentHovered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawKnotHandle(knot.position, knot.rotation, SplineSelection.Contains(knot), controlId, false, activeSpline, mirroredTangentSelected, mirroredTangentHovered);
|
||||
}
|
||||
|
||||
internal static void DrawKnotHandle(Vector3 knotPosition, Quaternion knotRotation, bool selected, int controlId,
|
||||
bool preview = false, bool activeSpline = true, bool mirroredTangentSelected = false, bool mirroredTangentHovered = false)
|
||||
{
|
||||
if(Event.current.GetTypeForControl(controlId) != EventType.Repaint)
|
||||
return;
|
||||
|
||||
var knotColor = s_KnotColor.value;
|
||||
if(preview)
|
||||
knotColor = Color.Lerp(Color.gray, Color.white, 0.5f);
|
||||
else if(selected)
|
||||
knotColor = Handles.selectedColor;
|
||||
else if(controlId > 0 && GUIUtility.hotControl == 0 && HandleUtility.nearestControl == controlId)
|
||||
knotColor = Handles.preselectionColor;
|
||||
|
||||
if(!activeSpline)
|
||||
knotColor = Handles.secondaryColor;
|
||||
|
||||
var handleSize = HandleUtility.GetHandleSize(knotPosition);
|
||||
var hovered = HandleUtility.nearestControl == controlId;
|
||||
|
||||
using (new Handles.DrawingScope(knotColor, Matrix4x4.TRS(knotPosition, knotRotation, Vector3.one)))
|
||||
{
|
||||
// Knot disc
|
||||
if (selected || hovered)
|
||||
{
|
||||
var radius = selected ? k_KnotDiscRadiusFactorSelected : k_KnotDiscRadiusFactorHover;
|
||||
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, radius * handleSize);
|
||||
}
|
||||
else
|
||||
Handles.DrawWireDisc(Vector3.zero, Vector3.up, k_KnotDiscRadiusFactorDefault * handleSize, k_HandleWidthHover * k_AliasedLineSizeMultiplier);
|
||||
}
|
||||
|
||||
var rotationDiscColor = knotColor;
|
||||
if (!selected && mirroredTangentSelected)
|
||||
rotationDiscColor = Handles.selectedColor;
|
||||
|
||||
using (new Handles.DrawingScope(rotationDiscColor, Matrix4x4.TRS(knotPosition, knotRotation, Vector3.one)))
|
||||
{
|
||||
// Knot rotation indicators
|
||||
var rotationDiscWidth = k_KnotRotDiscWidthDefault;
|
||||
if (selected || mirroredTangentSelected)
|
||||
rotationDiscWidth = k_KnotRotDiscWidthSelected;
|
||||
else if (hovered || mirroredTangentHovered)
|
||||
rotationDiscWidth = k_KnotRotDiscWidthHover;
|
||||
|
||||
DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * handleSize, rotationDiscWidth);
|
||||
|
||||
s_SegmentBuffer[0] = Vector3.zero;
|
||||
s_SegmentBuffer[1] = Vector3.up * 2f * k_SizeFactor * handleSize;
|
||||
Handles.DrawAAPolyLine(k_HandleWidthDefault, s_SegmentBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DrawPreviewKnot(EditableKnot knot)
|
||||
{
|
||||
DrawKnotHandle(knot.position, knot.rotation, false, -1, true);
|
||||
}
|
||||
|
||||
internal static void DrawTangentHandle(EditableTangent tangent, int controlId = -1, bool activeHandle = true)
|
||||
{
|
||||
if(Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
var knotPos = tangent.owner.position;
|
||||
var tangentPos = tangent.position;
|
||||
|
||||
var tangentHandleSize = HandleUtility.GetHandleSize(tangentPos);
|
||||
|
||||
var tangentColor = s_KnotColor.value;
|
||||
var selected = SplineSelection.Contains(tangent);
|
||||
var hovered = HandleUtility.nearestControl == controlId;
|
||||
if (selected)
|
||||
tangentColor = Handles.selectedColor;
|
||||
else if (hovered)
|
||||
tangentColor = Handles.preselectionColor;
|
||||
|
||||
if(!activeHandle)
|
||||
tangentColor = Handles.secondaryColor;
|
||||
|
||||
var tangentArmColor = tangentColor == s_KnotColor ? s_TangentColor.value : tangentColor;
|
||||
|
||||
var oppositeSelected = IsOppositeTangentSelected(tangent);
|
||||
if (tangentArmColor == s_TangentColor && oppositeSelected)
|
||||
tangentArmColor = Handles.selectedColor;
|
||||
|
||||
var oppositeHovered = IsOppositeTangentHovered(tangent);
|
||||
var mirrored = (tangent.owner is BezierEditableKnot bezierKnot) &&
|
||||
bezierKnot.mode == BezierEditableKnot.Mode.Mirrored;
|
||||
|
||||
using (new ColorScope(tangentArmColor))
|
||||
{
|
||||
var width = k_TangentLineWidthDefault;
|
||||
if (selected || (mirrored && oppositeSelected))
|
||||
width = k_TangentLineWidthSelected;
|
||||
else if (hovered || (mirrored && oppositeHovered))
|
||||
width = k_TangentLineWidthHover;
|
||||
|
||||
var tex = width > k_TangentLineWidthDefault ? s_ThickTangentLineAATex : null;
|
||||
|
||||
var startPos = knotPos;
|
||||
var toTangent = tangentPos - knotPos;
|
||||
var toTangentNorm = math.normalize(toTangent);
|
||||
var length = math.length(toTangent);
|
||||
|
||||
var knotHandleSize = HandleUtility.GetHandleSize(startPos);
|
||||
var knotHandleOffset = knotHandleSize * k_TangentStartOffsetFromKnot;
|
||||
var tangentHandleOffset = tangentHandleSize * k_tangentEndOffsetFromHandle;
|
||||
// Reduce the length slightly, so that there's some space between tangent line endings and handles.
|
||||
length = Mathf.Max(0f, length - knotHandleOffset - tangentHandleOffset);
|
||||
startPos += toTangentNorm * knotHandleOffset;
|
||||
SplineHandleUtility.DrawLineWithWidth(startPos + toTangentNorm * length, startPos, width, tex);
|
||||
}
|
||||
|
||||
var rotation = TransformOperation.CalculateElementSpaceHandleRotation(math.length(tangent.localPosition) >0 ? tangent : tangent.owner);
|
||||
using (new Handles.DrawingScope(tangentColor, Matrix4x4.TRS(tangent.position, rotation, Vector3.one)))
|
||||
{
|
||||
if (selected || hovered)
|
||||
{
|
||||
var radius = (selected ? k_KnotDiscRadiusFactorSelected : k_KnotDiscRadiusFactorHover) * tangentHandleSize;
|
||||
// As Handles.DrawSolidDisc has no thickness parameter, we're drawing a wire disc here so that the solid disc has thickness when viewed from a shallow angle.
|
||||
Handles.DrawWireDisc(Vector3.zero, Vector3.up, radius * 0.7f, k_HandleWidthHover);
|
||||
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, radius);
|
||||
}
|
||||
else
|
||||
Handles.DrawWireDisc(Vector3.zero, Vector3.up, k_KnotDiscRadiusFactorDefault * tangentHandleSize, k_HandleWidthHover * k_AliasedLineSizeMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawAAWireDisc(Vector3 position, Vector3 normal, float radius, float thickness)
|
||||
{
|
||||
// Right vector calculation here is identical to Handles.DrawWireDisc
|
||||
Vector3 right = Vector3.Cross(normal, Vector3.up);
|
||||
if ((double)right.sqrMagnitude < 1.0 / 1000.0)
|
||||
right = Vector3.Cross(normal, Vector3.right);
|
||||
|
||||
var angleStep = 360f / (s_AAWireDiscBuffer.Length - 1);
|
||||
for (int i = 0; i < s_AAWireDiscBuffer.Length - 1; i++)
|
||||
{
|
||||
s_AAWireDiscBuffer[i] = position + right * radius;
|
||||
right = Quaternion.AngleAxis(angleStep, normal) * right;
|
||||
}
|
||||
|
||||
s_AAWireDiscBuffer[s_AAWireDiscBuffer.Length - 1] = s_AAWireDiscBuffer[0];
|
||||
|
||||
var tex = thickness > 2f ? s_ThickTangentLineAATex : null;
|
||||
Handles.DrawAAPolyLine(tex, thickness, s_AAWireDiscBuffer);
|
||||
}
|
||||
|
||||
static bool IsOppositeTangentSelected(EditableTangent tangent)
|
||||
{
|
||||
return tangent.owner is BezierEditableKnot knot
|
||||
&& knot.mode != BezierEditableKnot.Mode.Broken
|
||||
&& knot.TryGetOppositeTangent(tangent, out EditableTangent oppositeTangent)
|
||||
&& SplineSelection.Contains(oppositeTangent);
|
||||
}
|
||||
|
||||
static bool IsOppositeTangentHovered(EditableTangent tangent)
|
||||
{
|
||||
return tangent.owner is BezierEditableKnot knot
|
||||
&& knot.TryGetOppositeTangent(tangent, out EditableTangent oppositeTangent)
|
||||
&& (s_LastHoveredTangent == oppositeTangent);
|
||||
}
|
||||
|
||||
internal static void DrawCurve(CurveData curve)
|
||||
{
|
||||
Event evt = Event.current;
|
||||
if (evt.type == EventType.Repaint)
|
||||
CurveHandleCap(curve, -1, EventType.Repaint);
|
||||
}
|
||||
|
||||
internal static void CurveHandleCap(CurveData curve, int controlID, EventType eventType, bool previewCurve = false, bool activeSpline = true)
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.Layout:
|
||||
{
|
||||
SplineHandleUtility.GetCurveSegments(curve, s_CurveSegmentsBuffer);
|
||||
|
||||
float dist = float.MaxValue;
|
||||
for (var i = 0; i < s_CurveSegmentsBuffer.Length - 1; ++i)
|
||||
{
|
||||
var a = s_CurveSegmentsBuffer[i];
|
||||
var b = s_CurveSegmentsBuffer[i + 1];
|
||||
dist = Mathf.Min(HandleUtility.DistanceToLine(a, b), dist);
|
||||
}
|
||||
|
||||
if (!Tools.viewToolActive)
|
||||
HandleUtility.AddControl(controlID, Mathf.Max(0, dist - k_PickingDistance));
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType.Repaint:
|
||||
{
|
||||
SplineHandleUtility.GetCurveSegments(curve, s_CurveSegmentsBuffer);
|
||||
//We attenuate the spline display if a spline can be controlled (id != -1) and
|
||||
//if it's not the current active spline
|
||||
var attenuate = controlID != -1 && !activeSpline;
|
||||
|
||||
var prevColor = Handles.color;
|
||||
|
||||
var color = s_LineNormalFrontColor.value;
|
||||
if (attenuate)
|
||||
color = Handles.secondaryColor;
|
||||
if (previewCurve)
|
||||
color.a *= k_PreviewCurveOpacity;
|
||||
|
||||
Handles.color = color;
|
||||
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
{
|
||||
Handles.DrawAAPolyLine(k_CurveLineWidth, s_CurveSegmentsBuffer);
|
||||
}
|
||||
|
||||
color = s_LineNormalBehindColor.value;
|
||||
if (attenuate)
|
||||
color = Handles.secondaryColor;
|
||||
if (previewCurve)
|
||||
color.a *= k_PreviewCurveOpacity;
|
||||
|
||||
Handles.color = color;
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
{
|
||||
Handles.DrawAAPolyLine(k_CurveLineWidth, s_CurveSegmentsBuffer);
|
||||
}
|
||||
|
||||
Handles.color = prevColor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fa11edcb768491b4b8b90ccf074e663f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue