first commit
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class CurveHandles
|
||||
{
|
||||
const float k_CurveLineWidth = 4f;
|
||||
const float k_PreviewCurveOpacity = 0.5f;
|
||||
|
||||
static readonly Vector3[] s_CurveDrawingBuffer = new Vector3[SplineCacheUtility.CurveDrawResolution + 1];
|
||||
static readonly Vector3[] s_FlowTriangleVertices = new Vector3[3];
|
||||
|
||||
/// <summary>
|
||||
/// Creates handles for a BezierCurve.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The controlID of the curve to create highlights for.</param>
|
||||
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
|
||||
public static void Draw(int controlID, BezierCurve curve)
|
||||
{
|
||||
if(Event.current.type == EventType.Repaint)
|
||||
Draw(controlID, curve, false, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates handles for a BezierCurve.
|
||||
/// </summary>
|
||||
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
|
||||
/// <param name="activeSpline">Whether the curve is part of the active spline.</param>
|
||||
internal static void Draw(BezierCurve curve, bool activeSpline)
|
||||
{
|
||||
if(Event.current.type == EventType.Repaint)
|
||||
Draw(0, curve, false, activeSpline);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates highlights for a BezierCurve to make it easier to select.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The controlID of the curve to create highlights for.</param>
|
||||
/// <param name="curve">The <see cref="BezierCurve"/> to create highlights for.</param>
|
||||
/// <param name="spline">The <see cref="ISpline"/> (if any) that the curve belongs to.</param>
|
||||
/// <param name="curveIndex">The curve's index if it belongs to a spline - otherwise -1.</param>
|
||||
/// <param name="knotA">The knot at the start of the curve.</param>
|
||||
/// <param name="knotB">The knot at the end of the curve.</param>
|
||||
/// <param name="activeSpline">Whether the curve is part of the active spline.</param>
|
||||
internal static void DrawWithHighlight(
|
||||
int controlID,
|
||||
ISpline spline,
|
||||
int curveIndex,
|
||||
float4x4 localToWorld,
|
||||
SelectableKnot knotA,
|
||||
SelectableKnot knotB,
|
||||
bool activeSpline)
|
||||
{
|
||||
var evt = Event.current;
|
||||
switch(evt.GetTypeForControl(controlID))
|
||||
{
|
||||
case EventType.Layout:
|
||||
case EventType.MouseMove:
|
||||
if (!SplineHandles.ViewToolActive() && activeSpline)
|
||||
{
|
||||
var curve = spline.GetCurve(curveIndex).Transform(localToWorld);
|
||||
var dist = DistanceToCurve(curve);
|
||||
HandleUtility.AddControl(controlID, Mathf.Max(0, dist - SplineHandleUtility.pickingDistance));
|
||||
//Trigger repaint on MouseMove to update highlight visuals from SplineHandles
|
||||
if (evt.type == EventType.MouseMove || controlID == HandleUtility.nearestControl)
|
||||
{
|
||||
SplineHandleUtility.GetNearestPointOnCurve(curve, out _, out var t);
|
||||
var curveMidT = EditorSplineUtility.GetCurveMiddleInterpolation(curve, spline, curveIndex);
|
||||
var hoveredKnot = t <= curveMidT ? knotA : knotB;
|
||||
|
||||
if (!(SplineHandleUtility.lastHoveredElement is SelectableKnot knot) || !knot.Equals(hoveredKnot))
|
||||
{
|
||||
if (GUIUtility.hotControl == 0 && HandleUtility.nearestControl == controlID)
|
||||
{
|
||||
SplineHandleUtility.SetLastHoveredElement(hoveredKnot, controlID);
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
var curveMD = spline.GetCurve(curveIndex).Transform(localToWorld);
|
||||
if (!SplineHandles.ViewToolActive() && HandleUtility.nearestControl == controlID)
|
||||
{
|
||||
//Clicking a knot selects it
|
||||
if (evt.button != 0)
|
||||
break;
|
||||
|
||||
GUIUtility.hotControl = controlID;
|
||||
evt.Use();
|
||||
|
||||
SplineHandleUtility.GetNearestPointOnCurve(curveMD, out _, out var t);
|
||||
SplineSelectionUtility.HandleSelection(t <= .5f ? knotA : knotB, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == controlID)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws flow on a BezierCurve to indicate the direction.
|
||||
/// </summary>
|
||||
/// <param name="curve">The <see cref="BezierCurve"/> to create highlights for.</param>
|
||||
/// <param name="spline">The <see cref="ISpline"/> (if any) that the curve belongs to.</param>
|
||||
/// <param name="curveIndex">The curve's index if it belongs to a spline - otherwise -1.</param>
|
||||
internal static void DrawFlow(BezierCurve curve, ISpline spline, int curveIndex)
|
||||
{
|
||||
if(Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
var arrow = SplineCacheUtility.GetCurveArrow(spline, curveIndex, curve);
|
||||
s_FlowTriangleVertices[0] = arrow.positions[0];
|
||||
s_FlowTriangleVertices[1] = arrow.positions[1];
|
||||
s_FlowTriangleVertices[2] = arrow.positions[2];
|
||||
|
||||
using (new Handles.DrawingScope(SplineHandleUtility.lineColor, arrow.trs))
|
||||
{
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
Handles.DrawAAConvexPolygon(s_FlowTriangleVertices);
|
||||
}
|
||||
|
||||
using (new Handles.DrawingScope(SplineHandleUtility.lineBehindColor, arrow.trs))
|
||||
{
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
Handles.DrawAAConvexPolygon(s_FlowTriangleVertices);
|
||||
}
|
||||
}
|
||||
|
||||
static void Draw(int controlID, BezierCurve curve, bool preview, bool activeSpline)
|
||||
{
|
||||
var evt = Event.current;
|
||||
|
||||
switch (evt.type)
|
||||
{
|
||||
case EventType.Layout:
|
||||
case EventType.MouseMove:
|
||||
if (!SplineHandles.ViewToolActive() && activeSpline)
|
||||
{
|
||||
var dist = DistanceToCurve(curve);
|
||||
HandleUtility.AddControl(controlID, Mathf.Max(0, dist - SplineHandleUtility.pickingDistance));
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.Repaint:
|
||||
var prevColor = Handles.color;
|
||||
FillCurveDrawingBuffer(curve);
|
||||
|
||||
var color = SplineHandleUtility.lineColor;
|
||||
if (preview)
|
||||
color.a *= k_PreviewCurveOpacity;
|
||||
|
||||
Handles.color = color;
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_CurveLineWidth, s_CurveDrawingBuffer);
|
||||
|
||||
color = SplineHandleUtility.lineBehindColor;
|
||||
if (preview)
|
||||
color.a *= k_PreviewCurveOpacity;
|
||||
|
||||
Handles.color = color;
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_CurveLineWidth, s_CurveDrawingBuffer);
|
||||
|
||||
Handles.color = prevColor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void FillCurveDrawingBuffer(BezierCurve curve)
|
||||
{
|
||||
SplineCacheUtility.GetCurvePositions(curve, s_CurveDrawingBuffer);
|
||||
}
|
||||
|
||||
internal static float DistanceToCurve(BezierCurve curve)
|
||||
{
|
||||
FillCurveDrawingBuffer(curve);
|
||||
return DistanceToCurve();
|
||||
}
|
||||
|
||||
static float DistanceToCurve()
|
||||
{
|
||||
float dist = float.MaxValue;
|
||||
for (var i = 0; i < s_CurveDrawingBuffer.Length - 1; ++i)
|
||||
{
|
||||
var a = s_CurveDrawingBuffer[i];
|
||||
var b = s_CurveDrawingBuffer[i + 1];
|
||||
dist = Mathf.Min(HandleUtility.DistanceToLine(a, b), dist);
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
internal static void DoCurveHighlightCap(SelectableKnot knot)
|
||||
{
|
||||
if(Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
if(knot.IsValid())
|
||||
{
|
||||
var spline = knot.SplineInfo.Spline;
|
||||
var localToWorld = knot.SplineInfo.LocalToWorld;
|
||||
|
||||
if(knot.KnotIndex > 0 || spline.Closed)
|
||||
{
|
||||
var curve = spline.GetCurve(spline.PreviousIndex(knot.KnotIndex)).Transform(localToWorld);
|
||||
var curveMiddleT = EditorSplineUtility.GetCurveMiddleInterpolation(curve, spline, spline.PreviousIndex(knot.KnotIndex));
|
||||
DrawCurveHighlight(curve, 1f, curveMiddleT);
|
||||
}
|
||||
|
||||
if(knot.KnotIndex < spline.Count - 1 || spline.Closed)
|
||||
{
|
||||
var curve = spline.GetCurve(knot.KnotIndex).Transform(localToWorld);
|
||||
var curveMiddleT = EditorSplineUtility.GetCurveMiddleInterpolation(curve, spline, knot.KnotIndex);
|
||||
DrawCurveHighlight(curve, 0f, curveMiddleT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawCurveHighlight(BezierCurve curve, float startT, float endT)
|
||||
{
|
||||
FillCurveDrawingBuffer(curve);
|
||||
|
||||
var growing = startT <= endT;
|
||||
var color = Handles.color;
|
||||
color.a = growing ? 1f : 0f;
|
||||
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
using (new Handles.DrawingScope(color))
|
||||
DrawAAPolyLineForCurveHighlight(color, startT, endT, 1f, growing);
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
using (new Handles.DrawingScope(color))
|
||||
DrawAAPolyLineForCurveHighlight(color, startT, endT, 0.3f, growing);
|
||||
}
|
||||
|
||||
static void DrawAAPolyLineForCurveHighlight(Color color, float startT, float endT, float colorAlpha, bool growing)
|
||||
{
|
||||
for (int i = 1; i <= SplineCacheUtility.CurveDrawResolution; ++i)
|
||||
{
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_CurveLineWidth, new[] { s_CurveDrawingBuffer[i - 1], s_CurveDrawingBuffer[i] });
|
||||
|
||||
var current = ((float)i / (float)SplineCacheUtility.CurveDrawResolution);
|
||||
if (growing)
|
||||
{
|
||||
if (current > endT)
|
||||
color.a = 0f;
|
||||
else if (current > startT)
|
||||
color.a = (1f - (current - startT) / (endT - startT)) * colorAlpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (current < endT)
|
||||
color.a = 0f;
|
||||
else if (current > endT && current < startT)
|
||||
color.a = (current - endT) / (startT - endT) * colorAlpha;
|
||||
}
|
||||
|
||||
Handles.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the set of control points that make up a curve.
|
||||
/// </summary>
|
||||
/// <param name="curve">The <see cref="BezierCurve"/> to create control points for.</param>
|
||||
public static void DrawControlNet(BezierCurve curve)
|
||||
{
|
||||
Handles.color = Color.green;
|
||||
Handles.DotHandleCap(-1, curve.P0, Quaternion.identity, HandleUtility.GetHandleSize(curve.P0) * .04f, Event.current.type);
|
||||
Handles.color = Color.red;
|
||||
Handles.DotHandleCap(-1, curve.P1, Quaternion.identity, HandleUtility.GetHandleSize(curve.P1) * .04f, Event.current.type);
|
||||
Handles.color = Color.yellow;
|
||||
Handles.DotHandleCap(-1, curve.P2, Quaternion.identity, HandleUtility.GetHandleSize(curve.P2) * .04f, Event.current.type);
|
||||
Handles.color = Color.blue;
|
||||
Handles.DotHandleCap(-1, curve.P3, Quaternion.identity, HandleUtility.GetHandleSize(curve.P3) * .04f, Event.current.type);
|
||||
|
||||
Handles.color = Color.gray;
|
||||
Handles.DrawDottedLine(curve.P0, curve.P1, 2f);
|
||||
Handles.DrawDottedLine(curve.P1, curve.P2, 2f);
|
||||
Handles.DrawDottedLine(curve.P2, curve.P3, 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77221e48184d50742984ffe3b4192f2a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,201 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEditor.SettingsManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class DirectManipulation
|
||||
{
|
||||
[UserSetting("Tweak Mode", "Plane Color")]
|
||||
static readonly Pref<Color> s_GuidePlaneColor = new Pref<Color>("Handles.DirectManipulation.PlaneColor", new Color(1f, 1f, 1f, 5f/255f));
|
||||
|
||||
[UserSetting("Tweak Mode", "Snap to Guide Enabled")]
|
||||
static readonly Pref<bool> s_SnapToGuide = new Pref<bool>("Handles.DirectManipulation.SnapToGuide", true);
|
||||
|
||||
[UserSetting("Tweak Mode", "Snap to Guide Distance")]
|
||||
static readonly Pref<float> s_SnapToGuideDistance = new Pref<float>("Handles.DirectManipulation.SnapToGuideDistance", 7f);
|
||||
|
||||
static readonly Vector3[] s_VertexBuffer = new Vector3[4];
|
||||
|
||||
public static bool IsDragging => s_IsDragging;
|
||||
|
||||
static readonly Vector3 k_GuidePlaneZTestOffset = new Vector3(0.001f, 0.001f, 0.001f);
|
||||
static Vector3 s_InitialPosition;
|
||||
static Quaternion s_InitialRotation;
|
||||
static Vector2 s_InitialMousePosition;
|
||||
static bool s_IsDragging;
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
static bool IncrementalSnapActive => EditorSnapSettings.incrementalSnapActive;
|
||||
#else
|
||||
static bool IncrementalSnapActive => false;
|
||||
#endif
|
||||
|
||||
const float k_HandleColorAlphaFactor = 0.3f;
|
||||
|
||||
static bool ShouldMoveOnNormal(int controlId) => GUIUtility.hotControl == controlId && Event.current.alt;
|
||||
|
||||
public static void BeginDrag(Vector3 position, Quaternion rotation)
|
||||
{
|
||||
s_InitialPosition = position;
|
||||
s_InitialRotation = rotation;
|
||||
s_InitialMousePosition = Event.current.mousePosition;
|
||||
}
|
||||
|
||||
public static Vector3 UpdateDrag(int controlId)
|
||||
{
|
||||
var position = ShouldMoveOnNormal(controlId)
|
||||
? MoveOnNormal(Event.current.mousePosition, s_InitialPosition, s_InitialRotation)
|
||||
: MoveOnPlane(Event.current.mousePosition, s_InitialPosition, s_InitialRotation, IncrementalSnapActive);
|
||||
|
||||
s_IsDragging = true;
|
||||
return position;
|
||||
}
|
||||
|
||||
public static void EndDrag()
|
||||
{
|
||||
s_IsDragging = false;
|
||||
}
|
||||
|
||||
public static void DrawHandles(int controlId, Vector3 position)
|
||||
{
|
||||
if (GUIUtility.hotControl != controlId || !s_IsDragging)
|
||||
return;
|
||||
|
||||
EditorGUIUtility.AddCursorRect(new Rect(0, 0, 100000, 10000), MouseCursor.MoveArrow);
|
||||
|
||||
if (ShouldMoveOnNormal(controlId))
|
||||
{
|
||||
var yDir = s_InitialRotation * Vector3.up;
|
||||
DrawGuideAxis(s_InitialPosition, yDir, Handles.yAxisColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
var zDir = s_InitialRotation * Vector3.forward;
|
||||
var xDir = s_InitialRotation * Vector3.right;
|
||||
|
||||
DrawGuidePlane(s_InitialPosition, xDir, zDir, position);
|
||||
DrawGuideDottedLine(s_InitialPosition, zDir, position);
|
||||
DrawGuideDottedLine(s_InitialPosition, xDir, position);
|
||||
|
||||
DrawGuideAxis(s_InitialPosition, zDir, Handles.zAxisColor);
|
||||
DrawGuideAxis(s_InitialPosition, xDir, Handles.xAxisColor);
|
||||
}
|
||||
}
|
||||
|
||||
static (Vector3 projection, float distance) GetSnapToGuideData(Vector3 current, Vector3 origin, Vector3 axis)
|
||||
{
|
||||
var projection = Vector3.Project(current - origin, axis);
|
||||
var screenPos = HandleUtility.WorldToGUIPoint(origin + projection);
|
||||
var distance = Vector2.Distance(screenPos, Event.current.mousePosition);
|
||||
return (projection, distance);
|
||||
}
|
||||
|
||||
static Vector3 MoveOnPlane(Vector2 mousePosition, Vector3 origin, Quaternion rotation, bool snapping)
|
||||
{
|
||||
var ray = HandleUtility.GUIPointToWorldRay(mousePosition);
|
||||
var manipPlane = new Plane(rotation * Vector3.up, origin);
|
||||
var position = manipPlane.Raycast(ray, out float distance)
|
||||
? ray.origin + ray.direction * distance
|
||||
: origin;
|
||||
|
||||
var dir = position - origin;
|
||||
var forward = GetSnapToGuideData(position, origin, rotation * Vector3.forward);
|
||||
var right = GetSnapToGuideData(position, origin, rotation * Vector3.right);
|
||||
|
||||
if (!snapping && s_SnapToGuide)
|
||||
{
|
||||
if (forward.distance < s_SnapToGuideDistance || right.distance < s_SnapToGuideDistance)
|
||||
{
|
||||
var snapToForward = forward.distance < right.distance;
|
||||
var axis = (snapToForward ? forward : right).projection;
|
||||
return origin + axis;
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.Approximately(dir.magnitude, 0f))
|
||||
dir = Vector3.forward;
|
||||
|
||||
var translation = Handles.SnapValue(Quaternion.Inverse(rotation) * dir, new Vector3(EditorSnapSettings.move.x, 0, EditorSnapSettings.move.z));
|
||||
return origin + rotation * translation;
|
||||
}
|
||||
|
||||
static Vector3 MoveOnNormal(Vector2 mousePosition, Vector3 origin, Quaternion rotation)
|
||||
{
|
||||
var upAxis = rotation * Vector3.up;
|
||||
var translation = upAxis * Handles.SnapValue(HandleUtility.CalcLineTranslation(s_InitialMousePosition, mousePosition, origin, upAxis), EditorSnapSettings.move.y);
|
||||
return origin + translation;
|
||||
}
|
||||
|
||||
static void DrawGuideAxis(Vector3 origin, Vector3 axis, Color color)
|
||||
{
|
||||
var start = origin - axis.normalized * 10000f;
|
||||
var end = origin + axis.normalized * 10000f;
|
||||
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
using (new Handles.DrawingScope(color))
|
||||
{
|
||||
Handles.DrawLine(origin, start, 0f);
|
||||
Handles.DrawLine(origin, end, 0f);
|
||||
}
|
||||
|
||||
color = new Color(color.r, color.g, color.b, color.a * k_HandleColorAlphaFactor);
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
using (new Handles.DrawingScope(color))
|
||||
{
|
||||
Handles.DrawLine(origin, start, 0f);
|
||||
Handles.DrawLine(origin, end, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawGuidePlane(Vector3 origin, Vector3 axisX, Vector3 axisZ, Vector3 position)
|
||||
{
|
||||
var xAxisProjection = Vector3.Project(position - origin, axisX);
|
||||
var zAxisProjection = Vector3.Project(position - origin, axisZ);
|
||||
var cross = math.cross(xAxisProjection, zAxisProjection);
|
||||
var normal = math.normalizesafe(cross);
|
||||
var scaledOffset = k_GuidePlaneZTestOffset * HandleUtility.GetHandleSize(origin);
|
||||
var calculatedOffset = new Vector3(scaledOffset.x * normal.x, scaledOffset.y * normal.y, scaledOffset.z * normal.z);
|
||||
|
||||
position += calculatedOffset;
|
||||
origin += calculatedOffset;
|
||||
|
||||
s_VertexBuffer[0] = origin;
|
||||
s_VertexBuffer[1] = origin + Vector3.Project(position - origin, axisX);
|
||||
s_VertexBuffer[2] = position;
|
||||
s_VertexBuffer[3] = origin + Vector3.Project(position - origin, axisZ);
|
||||
|
||||
DrawGuidePlane(Matrix4x4.identity);
|
||||
}
|
||||
|
||||
static void DrawGuidePlane(Matrix4x4 matrix)
|
||||
{
|
||||
var color = s_GuidePlaneColor.value;
|
||||
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
using (new Handles.DrawingScope(matrix))
|
||||
Handles.DrawSolidRectangleWithOutline(s_VertexBuffer, color, Color.clear);
|
||||
|
||||
color = new Color(s_GuidePlaneColor.value.r, s_GuidePlaneColor.value.g, s_GuidePlaneColor.value.b,
|
||||
s_GuidePlaneColor.value.a * k_HandleColorAlphaFactor);
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
using (new Handles.DrawingScope(matrix))
|
||||
Handles.DrawSolidRectangleWithOutline(s_VertexBuffer, color, Color.clear);
|
||||
}
|
||||
|
||||
static void DrawGuideDottedLine(Vector3 origin, Vector3 axis, Vector3 position)
|
||||
{
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
Handles.DrawDottedLine(origin + Vector3.Project(position - origin, axis), position, 3f);
|
||||
|
||||
var color = new Color(Handles.color.r, Handles.color.g, Handles.color.b, Handles.color.a * k_HandleColorAlphaFactor);
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
using (new Handles.DrawingScope(color))
|
||||
Handles.DrawDottedLine(origin + Vector3.Project(position - origin, axis), position, 3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 485daebcb9b4a264ba4f878081549056
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,322 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class KnotHandles
|
||||
{
|
||||
const float k_ColorAlphaFactor = 0.3f;
|
||||
const float k_KnotRotDiscRadius = 0.18f;
|
||||
const float k_KnotRotDiscWidthDefault = 1.5f;
|
||||
const float k_KnotRotDiscWidthHover = 3f;
|
||||
const float k_KnotHandleWidth = 2f;
|
||||
|
||||
static readonly List<SelectableKnot> k_KnotBuffer = new List<SelectableKnot>();
|
||||
|
||||
static readonly Vector3[] k_HandlePoints = new Vector3[11];
|
||||
|
||||
static List<(SelectableKnot knot, bool selected, bool hovered, Color knotColor, Color discColor, bool linkedKnot)> s_Knots = new ();
|
||||
|
||||
internal static void Do(int controlId, SelectableKnot knot, bool selected = false, bool hovered = false)
|
||||
{
|
||||
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
|
||||
return;
|
||||
|
||||
//Hovered might not be available if a TRS tool is in use
|
||||
hovered &= SplineHandleUtility.IsHoverAvailableForSplineElement();
|
||||
|
||||
var knotColor = SplineHandleUtility.elementColor;
|
||||
var rotationDiscColor = SplineHandleUtility.elementPreselectionColor;
|
||||
if (hovered)
|
||||
knotColor = SplineHandleUtility.elementPreselectionColor;
|
||||
else if (selected)
|
||||
knotColor = SplineHandleUtility.elementSelectionColor;
|
||||
|
||||
Draw(knot.Position, knot.Rotation, knotColor, selected, hovered, rotationDiscColor, k_KnotRotDiscWidthHover);
|
||||
DrawKnotIndices(knot);
|
||||
}
|
||||
|
||||
internal static void Draw(int controlId, SelectableKnot knot)
|
||||
{
|
||||
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
|
||||
return;
|
||||
|
||||
if(!knot.IsValid())
|
||||
return;
|
||||
|
||||
var selected = SplineSelection.Contains(knot);
|
||||
var knotHovered = SplineHandleUtility.IsElementHovered(controlId);
|
||||
|
||||
//Retrieving linked knots
|
||||
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
|
||||
var drawLinkedKnotHandle = k_KnotBuffer.Count != 1;
|
||||
var mainKnot = knot;
|
||||
|
||||
SelectableKnot lastHovered = new SelectableKnot();
|
||||
// Retrieving the last hovered element
|
||||
// SplineHandleUtility.lastHoveredElement is pointing either to:
|
||||
// - the hovered Knot and the ID is pointing to the controlID of that knot in that case
|
||||
// - if a curve is hovered, the element is the knot closest to the hovered part of the curve (start or end knot depending)
|
||||
// and the controlID is the one of the curve
|
||||
var lastHoveredElementIsKnot = SplineHandleUtility.lastHoveredElement is SelectableKnot;
|
||||
if (lastHoveredElementIsKnot)
|
||||
lastHovered = (SelectableKnot)SplineHandleUtility.lastHoveredElement;
|
||||
|
||||
var isCurveId = SplineHandles.IsCurveId(SplineHandleUtility.lastHoveredElementId);
|
||||
|
||||
var curveIsHovered = lastHoveredElementIsKnot &&
|
||||
k_KnotBuffer.Contains(lastHovered) &&
|
||||
isCurveId;
|
||||
|
||||
var hovered = knotHovered || (curveIsHovered && knot.Equals(lastHovered));
|
||||
|
||||
if (drawLinkedKnotHandle)
|
||||
{
|
||||
if (curveIsHovered)
|
||||
{
|
||||
drawLinkedKnotHandle = false;
|
||||
if (!knot.Equals(lastHovered))
|
||||
{
|
||||
if (!SplineSelection.Contains(knot))
|
||||
return;
|
||||
|
||||
hovered = false;
|
||||
mainKnot = lastHovered;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var linkedKnot in k_KnotBuffer)
|
||||
{
|
||||
if (!hovered)
|
||||
{
|
||||
var kSelected = SplineSelection.Contains(linkedKnot);
|
||||
|
||||
// If the current knot in not selected but other linked knots are, skip rendering
|
||||
if (!selected && kSelected)
|
||||
return;
|
||||
|
||||
// If current knot is selected but not k, don't consider k as a potential knot
|
||||
if (selected && !kSelected)
|
||||
{
|
||||
drawLinkedKnotHandle = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Main knot is the older one, the one on the spline of lowest range and the knot of lowest index
|
||||
if ((!SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(linkedKnot.SplineInfo)) &&
|
||||
(linkedKnot.SplineInfo.Index < mainKnot.SplineInfo.Index ||
|
||||
linkedKnot.SplineInfo.Index == mainKnot.SplineInfo.Index && linkedKnot.KnotIndex < mainKnot.KnotIndex))
|
||||
mainKnot = linkedKnot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Hovered might not be available if a TRS tool is in use
|
||||
hovered &= SplineHandleUtility.IsHoverAvailableForSplineElement();
|
||||
|
||||
var knotColor = SplineHandleUtility.elementColor;
|
||||
var highlightColor = SplineHandleUtility.elementPreselectionColor;
|
||||
var rotationDiscColor = SplineHandleUtility.elementPreselectionColor;
|
||||
if (hovered)
|
||||
{
|
||||
knotColor = SplineHandleUtility.elementPreselectionColor;
|
||||
highlightColor = SplineHandleUtility.elementPreselectionColor;
|
||||
}
|
||||
else if (selected)
|
||||
{
|
||||
knotColor = SplineHandleUtility.elementSelectionColor;
|
||||
highlightColor = SplineHandleUtility.elementSelectionColor;
|
||||
}
|
||||
|
||||
if (SplineHandleUtility.canDrawOnCurves && (hovered || selected))
|
||||
{
|
||||
using (new Handles.DrawingScope(highlightColor))
|
||||
CurveHandles.DoCurveHighlightCap(knot);
|
||||
}
|
||||
|
||||
if (knot.Equals(mainKnot))
|
||||
{
|
||||
s_Knots.Add((knot, selected, hovered, knotColor, rotationDiscColor, drawLinkedKnotHandle));
|
||||
DrawKnotIndices(knot);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ClearVisibleKnots()
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
s_Knots.Clear();
|
||||
}
|
||||
|
||||
internal static void DrawVisibleKnots()
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
foreach (var knotInfo in s_Knots)
|
||||
Draw(knotInfo.knot.Position, knotInfo.knot.Rotation, knotInfo.knotColor, knotInfo.selected, knotInfo.hovered, knotInfo.discColor, k_KnotRotDiscWidthHover, knotInfo.linkedKnot);
|
||||
}
|
||||
|
||||
static void DrawKnotIndices(SelectableKnot knot)
|
||||
{
|
||||
if (!SplineHandleSettings.ShowKnotIndices)
|
||||
return;
|
||||
|
||||
var hasLinkedKnots = !(k_KnotBuffer.Count == 1 && k_KnotBuffer.Contains(knot));
|
||||
if (k_KnotBuffer != null && k_KnotBuffer.Count > 0 && hasLinkedKnots)
|
||||
{
|
||||
var stringBuilder = new System.Text.StringBuilder("[");
|
||||
for (var i = 0; i < k_KnotBuffer.Count; i++)
|
||||
{
|
||||
stringBuilder.Append($"({k_KnotBuffer[i].SplineInfo.Index},{k_KnotBuffer[i].KnotIndex})");
|
||||
if (i != k_KnotBuffer.Count - 1)
|
||||
stringBuilder.Append(", ");
|
||||
}
|
||||
|
||||
stringBuilder.Append("]");
|
||||
Handles.Label(knot.Position, stringBuilder.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
Handles.Label(knot.Position, $"[{knot.KnotIndex}]");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Draw(SelectableKnot knot, Color knotColor, bool selected, bool hovered)
|
||||
{
|
||||
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
|
||||
var mainKnot = knot;
|
||||
if(k_KnotBuffer.Count != 1)
|
||||
{
|
||||
foreach(var k in k_KnotBuffer)
|
||||
{
|
||||
//Main knot is the older one, the one on the spline of lowest range and the knot of lowest index
|
||||
if(k.SplineInfo.Index < mainKnot.SplineInfo.Index ||
|
||||
k.SplineInfo.Index == mainKnot.SplineInfo.Index && k.KnotIndex < mainKnot.KnotIndex)
|
||||
mainKnot = k;
|
||||
}
|
||||
}
|
||||
if(!mainKnot.Equals(knot))
|
||||
return;
|
||||
|
||||
Draw(knot.Position, knot.Rotation, knotColor, selected, hovered, knotColor, k_KnotRotDiscWidthDefault, k_KnotBuffer.Count != 1);
|
||||
}
|
||||
|
||||
internal static void Draw(Vector3 position, Quaternion rotation, Color knotColor, bool selected, bool hovered)
|
||||
{
|
||||
Draw(position, rotation, knotColor, selected, hovered, knotColor, k_KnotRotDiscWidthDefault);
|
||||
}
|
||||
|
||||
static void UpdateHandlePoints(float size)
|
||||
{
|
||||
var startIndex = 5;
|
||||
k_HandlePoints[startIndex] = Vector3.forward * k_KnotRotDiscRadius * size;
|
||||
var r = Vector3.right * SplineHandleUtility.knotDiscRadiusFactorDefault * size;
|
||||
|
||||
//The first and last element should be in the middle of the points list to get a better visual
|
||||
for(int i = 0; i < 9; i++)
|
||||
{
|
||||
var index = ( i + startIndex + 1 ) % k_HandlePoints.Length;
|
||||
var pos = Quaternion.Euler(0, ( 1f - i / 8f ) * 180f, 0) * r;
|
||||
k_HandlePoints[index] = pos;
|
||||
if(index == k_HandlePoints.Length - 1)
|
||||
{
|
||||
startIndex += 1;
|
||||
k_HandlePoints[0] = pos;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal static void DrawInformativeKnot(SelectableKnot knot, float sizeFactor = 0.5f)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
|
||||
var drawLinkedKnotHandle = k_KnotBuffer.Count != 1;
|
||||
|
||||
if(drawLinkedKnotHandle)
|
||||
{
|
||||
foreach(var k in k_KnotBuffer)
|
||||
{
|
||||
//If the current knot in not selected but other linked knots are, skip rendering
|
||||
if(SplineSelection.Contains(k))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DrawInformativeKnotVisual(knot, SplineHandleUtility.lineColor, sizeFactor);
|
||||
}
|
||||
|
||||
static void DrawInformativeKnotVisual(SelectableKnot knot, Color knotColor, float sizeFactor = 0.5f)
|
||||
{
|
||||
var position = knot.Position;
|
||||
var size = HandleUtility.GetHandleSize(position);
|
||||
using(new Handles.DrawingScope(knotColor, Matrix4x4.TRS(position, knot.Rotation, Vector3.one)))
|
||||
{
|
||||
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, size * SplineHandleUtility.knotDiscRadiusFactorSelected * sizeFactor);
|
||||
}
|
||||
}
|
||||
|
||||
static void Draw(Vector3 position, Quaternion rotation, Color knotColor, bool selected, bool hovered, Color discColor, float rotationDiscWidth, bool linkedKnots = false)
|
||||
{
|
||||
var size = HandleUtility.GetHandleSize(position);
|
||||
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
{
|
||||
using (new Handles.DrawingScope(knotColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
DrawKnotShape(size, selected, linkedKnots);
|
||||
|
||||
if (hovered)
|
||||
{
|
||||
using (new Handles.DrawingScope(discColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * size, rotationDiscWidth);
|
||||
}
|
||||
}
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
{
|
||||
var newKnotColor = new Color(knotColor.r, knotColor.g, knotColor.b, knotColor.a * k_ColorAlphaFactor);
|
||||
using (new Handles.DrawingScope(newKnotColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
DrawKnotShape(size, selected, linkedKnots);
|
||||
|
||||
if (hovered)
|
||||
{
|
||||
var newDiscColor = new Color(discColor.r, discColor.g,
|
||||
discColor.b, discColor.a * k_ColorAlphaFactor);
|
||||
using (new Handles.DrawingScope(newDiscColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * size, rotationDiscWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawKnotShape(float size, bool selected, bool linkedKnots)
|
||||
{
|
||||
if (!linkedKnots)
|
||||
{
|
||||
UpdateHandlePoints(size);
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_KnotHandleWidth, k_HandlePoints);
|
||||
if (selected)
|
||||
Handles.DrawAAConvexPolygon(k_HandlePoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Knot disc
|
||||
if (selected)
|
||||
{
|
||||
var radius = selected ? SplineHandleUtility.knotDiscRadiusFactorSelected : SplineHandleUtility.knotDiscRadiusFactorHover;
|
||||
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, radius * size);
|
||||
}
|
||||
else
|
||||
Handles.DrawWireDisc(Vector3.zero, Vector3.up, SplineHandleUtility.knotDiscRadiusFactorDefault * size, SplineHandleUtility.handleWidth * SplineHandleUtility.aliasedLineSizeMultiplier);
|
||||
}
|
||||
|
||||
Handles.DrawAAPolyLine(Vector3.zero, Vector3.up * 2f * SplineHandleUtility.sizeFactor * size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b96c3a8372a8f9945bc2e2b906ac79a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,228 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
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<ISelectableElement> s_SplineElementsCompareSet = new HashSet<ISelectableElement>();
|
||||
static readonly List<ISelectableElement> s_SplineElementsBuffer = new List<ISelectableElement>();
|
||||
static readonly HashSet<ISelectableElement> s_PreRectSelectionElements = new HashSet<ISelectableElement>();
|
||||
|
||||
public void OnGUI(IReadOnlyList<SplineInfo> splines)
|
||||
{
|
||||
int id = GUIUtility.GetControlID(FocusType.Passive);
|
||||
Event evt = Event.current;
|
||||
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.Layout:
|
||||
case EventType.MouseMove:
|
||||
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, splines);
|
||||
}
|
||||
}
|
||||
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 (SplineHandles.ViewToolActive())
|
||||
return;
|
||||
|
||||
if (HandleUtility.nearestControl == id && evt.button == 0)
|
||||
{
|
||||
m_StartPos = evt.mousePosition;
|
||||
m_Rect = new Rect(Vector3.zero, Vector2.zero);
|
||||
|
||||
BeginSelection(splines);
|
||||
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, splines);
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
|
||||
EndSelection(m_Rect, splines);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BeginSelection(IReadOnlyList<SplineInfo> splines)
|
||||
{
|
||||
RefreshSelectionMode();
|
||||
m_InitialMode = m_Mode;
|
||||
|
||||
s_SplineElementsCompareSet.Clear();
|
||||
s_SplineElementsBuffer.Clear();
|
||||
if (m_Mode == Mode.Replace)
|
||||
{
|
||||
SplineSelection.Clear();
|
||||
s_PreRectSelectionElements.Clear();
|
||||
}
|
||||
else
|
||||
SplineSelection.GetElements(splines, s_PreRectSelectionElements);
|
||||
}
|
||||
|
||||
void UpdateSelection(Rect rect, IReadOnlyList<SplineInfo> splines)
|
||||
{
|
||||
//Get all elements in rect
|
||||
s_SplineElementsBuffer.Clear();
|
||||
for (int i = 0; i < splines.Count; ++i)
|
||||
{
|
||||
var splineData = splines[i];
|
||||
for (int j = 0; j < splineData.Spline.Count; ++j)
|
||||
if(!SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(splineData))
|
||||
GetElementSelection(rect, splineData, 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, SplineInfo splineInfo, int index, List<ISelectableElement> results)
|
||||
{
|
||||
var knot = splineInfo.Spline[index];
|
||||
var localToWorld = splineInfo.LocalToWorld;
|
||||
var worldKnot = knot.Transform(localToWorld);
|
||||
Vector3 screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position);
|
||||
|
||||
if (screenSpace.z > 0 && rect.Contains(screenSpace))
|
||||
results.Add(new SelectableKnot(splineInfo, index));
|
||||
|
||||
var tangentIn = new SelectableTangent(splineInfo, index, BezierTangent.In);
|
||||
if (SplineSelectionUtility.IsSelectable(tangentIn))
|
||||
{
|
||||
screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position + math.rotate(worldKnot.Rotation, worldKnot.TangentIn));
|
||||
if (screenSpace.z > 0 && rect.Contains(screenSpace))
|
||||
results.Add(tangentIn);
|
||||
}
|
||||
|
||||
var tangentOut = new SelectableTangent(splineInfo, index, BezierTangent.Out);
|
||||
if (SplineSelectionUtility.IsSelectable(tangentOut))
|
||||
{
|
||||
screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position + math.rotate(worldKnot.Rotation, worldKnot.TangentOut));
|
||||
if (screenSpace.z > 0 && rect.Contains(screenSpace))
|
||||
results.Add(tangentOut);
|
||||
}
|
||||
}
|
||||
|
||||
void EndSelection(Rect rect, IReadOnlyList<SplineInfo> splines)
|
||||
{
|
||||
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,68 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor.SettingsManagement;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class SplineHandleSettings
|
||||
{
|
||||
[UserSetting]
|
||||
static readonly Pref<bool> s_FlowDirectionEnabled = new Pref<bool>("Handles.FlowDirectionEnabled", true);
|
||||
|
||||
[UserSetting]
|
||||
static readonly Pref<bool> s_ShowAllTangents = new Pref<bool>("Handles.ShowAllTangents", true);
|
||||
|
||||
static readonly Pref<bool> s_ShowKnotIndices = new Pref<bool>("Handles.ShowKnotIndices", false);
|
||||
|
||||
[UserSetting]
|
||||
static UserSetting<bool> s_ShowMesh = new UserSetting<bool>(PathSettings.instance,"Handles.Debug.ShowMesh", false, SettingsScope.User);
|
||||
[UserSetting]
|
||||
static UserSetting<Color> s_MeshColor = new UserSetting<Color>(PathSettings.instance, "Handles.Debug.MeshColor", Color.white, SettingsScope.User);
|
||||
[UserSetting]
|
||||
static UserSetting<float> s_MeshSize = new UserSetting<float>(PathSettings.instance, "Handles.Debug.MeshSize", 0.1f, SettingsScope.User);
|
||||
[UserSetting]
|
||||
static UserSetting<int> s_MeshResolution = new UserSetting<int>(PathSettings.instance, "Handles.Debug.MeshResolution", SplineUtility.DrawResolutionDefault, SettingsScope.User);
|
||||
|
||||
[UserSettingBlock("Spline Mesh")]
|
||||
static void HandleDebugPreferences(string searchContext)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
s_MeshColor.value = SettingsGUILayout.SettingsColorField("Color", s_MeshColor, searchContext);
|
||||
s_MeshSize.value = SettingsGUILayout.SettingsSlider("Size", s_MeshSize, 0.01f, 1f, searchContext);
|
||||
s_MeshResolution.value = SettingsGUILayout.SettingsSlider("Resolution", s_MeshResolution, 4, 100, searchContext);
|
||||
|
||||
if(EditorGUI.EndChangeCheck())
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
public static bool FlowDirectionEnabled
|
||||
{
|
||||
get => s_FlowDirectionEnabled;
|
||||
set => s_FlowDirectionEnabled.SetValue(value);
|
||||
}
|
||||
|
||||
public static bool ShowAllTangents
|
||||
{
|
||||
get => s_ShowAllTangents;
|
||||
set => s_ShowAllTangents.SetValue(value);
|
||||
}
|
||||
|
||||
public static bool ShowKnotIndices
|
||||
{
|
||||
get => s_ShowKnotIndices;
|
||||
set => s_ShowKnotIndices.SetValue(value);
|
||||
}
|
||||
|
||||
public static bool ShowMesh
|
||||
{
|
||||
get => s_ShowMesh;
|
||||
set => s_ShowMesh.SetValue(value);
|
||||
}
|
||||
|
||||
public static Color SplineMeshColor => s_MeshColor;
|
||||
public static float SplineMeshSize => s_MeshSize;
|
||||
public static int SplineMeshResolution => s_MeshResolution;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bff491709f26ca4d924ad2c5f505fb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,464 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides the ability to draw a handle for a spline.
|
||||
/// </summary>
|
||||
public static class SplineHandles
|
||||
{
|
||||
/// <summary>
|
||||
/// The scope used to draw a spline. This is managing several purposes when using SplineHandles.DrawSomething().
|
||||
/// This ensure selection is working properly, and that hovering an element is highlighting the correct related
|
||||
/// elements (for instance hovering a tangent highlights the opposite one when needed and the knot as well).
|
||||
/// </summary>
|
||||
public class SplineHandleScope : IDisposable
|
||||
{
|
||||
int m_NearestControl;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a new scope to draw spline elements in.
|
||||
/// </summary>
|
||||
public SplineHandleScope()
|
||||
{
|
||||
m_NearestControl = HandleUtility.nearestControl;
|
||||
Clear();
|
||||
SplineHandleUtility.minElementId = GUIUtility.GetControlID(FocusType.Passive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called automatically when the `SplineHandleScope` is disposed.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
SplineHandleUtility.maxElementId = GUIUtility.GetControlID(FocusType.Passive);
|
||||
|
||||
var evtType = Event.current.type;
|
||||
if ( (evtType == EventType.MouseMove || evtType == EventType.Layout)
|
||||
&& HandleUtility.nearestControl == m_NearestControl)
|
||||
SplineHandleUtility.ResetLastHoveredElement();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color of sections of spline curve handles that are behind objects in the Scene view.
|
||||
/// </summary>
|
||||
public static Color lineBehindColor => SplineHandleUtility.lineBehindColor;
|
||||
|
||||
/// <summary>
|
||||
/// The color of sections of spline curves handles that are in front of objects in the Scene view.
|
||||
/// </summary>
|
||||
public static Color lineColor => SplineHandleUtility.lineColor;
|
||||
|
||||
/// <summary>
|
||||
/// The color of tangent handles for a spline.
|
||||
/// </summary>
|
||||
public static Color tangentColor => SplineHandleUtility.tangentColor;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to pick a spline knot, tangent, or curve handle at.
|
||||
/// </summary>
|
||||
public static float pickingDistance => SplineHandleUtility.pickingDistance;
|
||||
|
||||
static List<int> s_ControlIDs = new();
|
||||
static List<int> s_CurveIDs = new();
|
||||
|
||||
static readonly List<SelectableKnot> k_KnotBuffer = new ();
|
||||
static Dictionary<SelectableKnot, int> s_KnotsIDs = new ();
|
||||
|
||||
// todo Tools.viewToolActive should be handling the modifier check, but 2022.2 broke this
|
||||
internal static bool ViewToolActive()
|
||||
{
|
||||
return Tools.viewToolActive || Tools.current == Tool.View || (Event.current.modifiers & EventModifiers.Alt) == EventModifiers.Alt;
|
||||
}
|
||||
|
||||
static void Clear()
|
||||
{
|
||||
s_CurveIDs.Clear();
|
||||
s_KnotsIDs.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates handles for a set of splines. These handles display the knots, tangents, and segments of a spline.
|
||||
/// These handles support selection and the direct manipulation of spline elements.
|
||||
/// </summary>
|
||||
/// <param name="splines">The set of splines to draw handles for.</param>
|
||||
public static void DoHandles(IReadOnlyList<SplineInfo> splines)
|
||||
{
|
||||
Profiler.BeginSample("SplineHandles.DoHandles");
|
||||
using (new SplineHandleScope())
|
||||
{
|
||||
// Drawing done in two separate passes to make sure the curves are drawn behind the spline elements.
|
||||
// Draw the curves.
|
||||
for (int i = 0; i < splines.Count; ++i)
|
||||
{
|
||||
DoSegmentsHandles(splines[i]);
|
||||
}
|
||||
|
||||
DoKnotsAndTangentsHandles(splines);
|
||||
}
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
internal static bool IsCurveId(int id)
|
||||
{
|
||||
return s_CurveIDs.Contains(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates knot and tangent handles for a spline. Call `DoKnotsAndTangentsHandles` in a `SplineHandleScope`.
|
||||
/// This method is used internally by `DoHandles`.
|
||||
/// </summary>
|
||||
/// <param name="spline">The spline to create knot and tangent handles for.</param>
|
||||
public static void DoKnotsAndTangentsHandles(SplineInfo spline)
|
||||
{
|
||||
SplineHandleUtility.UpdateElementColors();
|
||||
KnotHandles.ClearVisibleKnots();
|
||||
// Draw the spline elements.
|
||||
DrawSplineElements(spline);
|
||||
//Drawing knots on top of all other elements and above other splines
|
||||
KnotHandles.DrawVisibleKnots();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates knot and tangent handles for multiple splines. Call `DoKnotsAndTangentsHandles` in a `SplineHandleScope`.
|
||||
/// This method is used internally by `DoHandles`.
|
||||
/// </summary>
|
||||
/// <param name="splines">The splines to create knot and tangent handles for.</param>
|
||||
public static void DoKnotsAndTangentsHandles(IReadOnlyList<SplineInfo> splines)
|
||||
{
|
||||
SplineHandleUtility.UpdateElementColors();
|
||||
KnotHandles.ClearVisibleKnots();
|
||||
// Draw the spline elements.
|
||||
for (int i = 0; i < splines.Count; ++i)
|
||||
DrawSplineElements(splines[i]);
|
||||
//Drawing knots on top of all other elements and above other splines
|
||||
KnotHandles.DrawVisibleKnots();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates segment handles for a spline. Call `DoCurvesHandles` in a `SplineHandleScope`.
|
||||
/// This method is used internally by `DrawHandles`.
|
||||
/// </summary>
|
||||
/// <param name="splineInfo">The splineInfo of the spline to draw knots and tangents for.</param>
|
||||
public static void DoSegmentsHandles(SplineInfo splineInfo)
|
||||
{
|
||||
var spline = splineInfo.Spline;
|
||||
if (spline == null || spline.Count < 2)
|
||||
return;
|
||||
|
||||
var localToWorld = splineInfo.LocalToWorld;
|
||||
|
||||
// If the spline isn't closed, skip the last index of the spline
|
||||
int lastIndex = spline.Closed ? spline.Count - 1 : spline.Count - 2;
|
||||
|
||||
if (SplineHandleSettings.ShowMesh)
|
||||
{
|
||||
using (var nativeSpline = new NativeSpline(spline, localToWorld))
|
||||
using (var mesh = new SplineMeshHandle<NativeSpline>())
|
||||
using (new ZTestScope(UnityEngine.Rendering.CompareFunction.Less))
|
||||
{
|
||||
mesh.Do(nativeSpline, SplineHandleSettings.SplineMeshSize, SplineHandleSettings.SplineMeshColor, SplineHandleSettings.SplineMeshResolution);
|
||||
}
|
||||
}
|
||||
|
||||
s_ControlIDs.Clear();
|
||||
for (int idIndex = 0; idIndex < lastIndex + 1; ++idIndex)
|
||||
{
|
||||
var id = GUIUtility.GetControlID(FocusType.Passive);
|
||||
s_ControlIDs.Add(id);
|
||||
s_CurveIDs.Add(id);
|
||||
}
|
||||
|
||||
var drawHandlesAsActive = !SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(splineInfo);
|
||||
|
||||
//Draw all the curves at once
|
||||
SplineCacheUtility.GetCachedPositions(spline, out var positions);
|
||||
|
||||
using (new Handles.DrawingScope(SplineHandleUtility.lineColor, localToWorld))
|
||||
{
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, 4f, positions);
|
||||
}
|
||||
|
||||
using (new Handles.DrawingScope(SplineHandleUtility.lineBehindColor, localToWorld))
|
||||
{
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, 4f, positions);
|
||||
}
|
||||
|
||||
if (drawHandlesAsActive)
|
||||
{
|
||||
for (int curveIndex = 0; curveIndex < lastIndex + 1; ++curveIndex)
|
||||
{
|
||||
if (SplineHandleSettings.FlowDirectionEnabled && Event.current.type == EventType.Repaint)
|
||||
{
|
||||
var curve = spline.GetCurve(curveIndex).Transform(localToWorld);
|
||||
CurveHandles.DrawFlow(curve, spline, curveIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int curveIndex = 0; curveIndex < lastIndex + 1; ++curveIndex)
|
||||
{
|
||||
CurveHandles.DrawWithHighlight(
|
||||
s_ControlIDs[curveIndex],
|
||||
spline,
|
||||
curveIndex,
|
||||
localToWorld,
|
||||
new SelectableKnot(splineInfo, curveIndex),
|
||||
new SelectableKnot(splineInfo, SplineUtility.NextIndex(curveIndex, spline.Count, spline.Closed)),
|
||||
drawHandlesAsActive);
|
||||
}
|
||||
|
||||
SplineHandleUtility.canDrawOnCurves = true;
|
||||
}
|
||||
|
||||
static void DrawSplineElements(SplineInfo splineInfo)
|
||||
{
|
||||
var spline = splineInfo.Spline;
|
||||
var drawHandlesAsActive = !SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(splineInfo);
|
||||
|
||||
if (drawHandlesAsActive)
|
||||
{
|
||||
for (int knotIndex = 0; knotIndex < spline.Count; ++knotIndex)
|
||||
DrawKnotWithTangentsHandles_Internal(new SelectableKnot(splineInfo, knotIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int knotIndex = 0; knotIndex < spline.Count; ++knotIndex)
|
||||
KnotHandles.DrawInformativeKnot(new SelectableKnot(splineInfo, knotIndex));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates handles for a knot and its tangents if those tangents are modifiable.
|
||||
/// These handles support the selection and direct manipulation of spline elements.
|
||||
/// Call `DoKnotWithTangentsHandles` in a `SplineHandleScope`.
|
||||
/// </summary>
|
||||
/// <param name="knot">The knot to draw handles for.</param>
|
||||
public static void DoKnotWithTangentsHandles(SelectableKnot knot)
|
||||
{
|
||||
KnotHandles.ClearVisibleKnots();
|
||||
DrawKnotWithTangentsHandles_Internal(knot);
|
||||
KnotHandles.DrawVisibleKnots();
|
||||
}
|
||||
|
||||
static void DrawKnotWithTangentsHandles_Internal(SelectableKnot knot)
|
||||
{
|
||||
var splineInfo = knot.SplineInfo;
|
||||
if (SplineUtility.AreTangentsModifiable(splineInfo.Spline.GetTangentMode(knot.KnotIndex)))
|
||||
DoTangentsHandles(knot);
|
||||
|
||||
DrawKnotHandles_Internal(knot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create handles for a knot. These handles the support selection and direct manipulation of spline elements.
|
||||
/// Call `DoKnotHandles` in a `SplineHandleScope`.
|
||||
/// </summary>
|
||||
/// <param name="knot">The knot to draw handles for.</param>
|
||||
public static void DoKnotHandles(SelectableKnot knot)
|
||||
{
|
||||
KnotHandles.ClearVisibleKnots();
|
||||
DrawKnotHandles_Internal(knot);
|
||||
KnotHandles.DrawVisibleKnots();
|
||||
}
|
||||
|
||||
static void DrawKnotHandles_Internal(SelectableKnot knot)
|
||||
{
|
||||
var id = GetKnotID(knot);
|
||||
SelectionHandle(id, knot);
|
||||
KnotHandles.Draw(id, knot);
|
||||
}
|
||||
/// <summary>
|
||||
/// Create handles for a knot's tangents if those tangents are modifiable. `DoTangentsHandles` does not create handles for the knot.
|
||||
/// These handles support the selection and direct manipulation of the spline elements.
|
||||
/// Call `DoTangentsHandles` in a `SplineHandleScope`.
|
||||
/// </summary>
|
||||
/// <param name="knot">The knot to draw tangent handles for.</param>
|
||||
public static void DoTangentsHandles(SelectableKnot knot)
|
||||
{
|
||||
if(!knot.IsValid())
|
||||
return;
|
||||
|
||||
var splineInfo = knot.SplineInfo;
|
||||
var spline = splineInfo.Spline;
|
||||
var knotIndex = knot.KnotIndex;
|
||||
|
||||
var tangentIn = new SelectableTangent(splineInfo, knotIndex, BezierTangent.In);
|
||||
var tangentOut = new SelectableTangent(splineInfo, knotIndex, BezierTangent.Out);
|
||||
|
||||
var controlIdIn = GUIUtility.GetControlID(FocusType.Passive);
|
||||
var controlIdOut = GUIUtility.GetControlID(FocusType.Passive);
|
||||
// Tangent In
|
||||
if (GUIUtility.hotControl == controlIdIn || SplineHandleUtility.ShouldShowTangent(tangentIn) && (spline.Closed || knotIndex != 0))
|
||||
{
|
||||
SelectionHandle(controlIdIn, tangentIn);
|
||||
TangentHandles.Draw(controlIdIn, tangentIn);
|
||||
}
|
||||
// Tangent Out
|
||||
if (GUIUtility.hotControl == controlIdOut || SplineHandleUtility.ShouldShowTangent(tangentOut) && (spline.Closed || knotIndex + 1 != spline.Count))
|
||||
{
|
||||
SelectionHandle(controlIdOut, tangentOut);
|
||||
TangentHandles.Draw(controlIdOut, tangentOut);
|
||||
}
|
||||
}
|
||||
|
||||
static int GetKnotID(SelectableKnot knot)
|
||||
{
|
||||
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
|
||||
//If a linked knot as already been assigned, return the same id
|
||||
if (s_KnotsIDs.ContainsKey(k_KnotBuffer[0]))
|
||||
return s_KnotsIDs[k_KnotBuffer[0]];
|
||||
|
||||
//else compute a new id and record it
|
||||
var id = GUIUtility.GetControlID(FocusType.Passive);
|
||||
s_KnotsIDs.Add(k_KnotBuffer[0], id);
|
||||
return id;
|
||||
}
|
||||
|
||||
static void SelectionHandle<T>(int id, T element)
|
||||
where T : struct, ISelectableElement
|
||||
{
|
||||
Event evt = Event.current;
|
||||
EventType eventType = evt.GetTypeForControl(id);
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.Layout:
|
||||
case EventType.MouseMove:
|
||||
if (!ViewToolActive())
|
||||
{
|
||||
HandleUtility.AddControl(id, SplineHandleUtility.DistanceToCircle(element.Position, SplineHandleUtility.pickingDistance));
|
||||
if (GUIUtility.hotControl == 0 && HandleUtility.nearestControl == id)
|
||||
SplineHandleUtility.SetLastHoveredElement(element, id);
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
if (HandleUtility.nearestControl == id && evt.button == 0)
|
||||
{
|
||||
GUIUtility.hotControl = id;
|
||||
evt.Use();
|
||||
|
||||
DirectManipulation.BeginDrag(element.Position, EditorSplineUtility.GetElementRotation(element));
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
EditorSplineUtility.RecordObject(element.SplineInfo, "Move Knot");
|
||||
var pos = TransformOperation.ApplySmartRounding(DirectManipulation.UpdateDrag(id));
|
||||
|
||||
if (element is SelectableTangent tangent)
|
||||
EditorSplineUtility.ApplyPositionToTangent(tangent, pos);
|
||||
else
|
||||
element.Position = pos;
|
||||
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id && evt.button == 0)
|
||||
{
|
||||
if (!DirectManipulation.IsDragging)
|
||||
SplineSelectionUtility.HandleSelection(element);
|
||||
|
||||
DirectManipulation.EndDrag();
|
||||
GUI.changed = true;
|
||||
|
||||
evt.Use();
|
||||
GUIUtility.hotControl = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.Repaint:
|
||||
DirectManipulation.DrawHandles(id, element.Position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a handle for a spline.
|
||||
/// </summary>
|
||||
/// <param name="spline">The target spline.</param>
|
||||
/// <typeparam name="T">A type implementing ISpline.</typeparam>
|
||||
public static void DoSpline<T>(T spline) where T : ISpline => DoSpline(-1, spline);
|
||||
|
||||
/// <summary>
|
||||
/// Draws a handle for a spline.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The spline mesh controlID.</param>
|
||||
/// <param name="spline">The target spline.</param>
|
||||
/// <typeparam name="T">A type implementing ISpline.</typeparam>
|
||||
public static void DoSpline<T>(int controlID, T spline) where T : ISpline
|
||||
{
|
||||
for(int i = 0; i < spline.GetCurveCount(); ++i)
|
||||
CurveHandles.Draw(controlID, spline.GetCurve(i));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a handle for a <see cref="BezierCurve"/>.
|
||||
/// </summary>
|
||||
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
|
||||
public static void DoCurve(BezierCurve curve) => CurveHandles.Draw(-1, curve);
|
||||
|
||||
/// <summary>
|
||||
/// Draws a handle for a <see cref="BezierCurve"/>.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The spline mesh controlID.</param>
|
||||
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
|
||||
public static void DoCurve(int controlID, BezierCurve curve) => CurveHandles.Draw(controlID, curve);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws handles for a knot. These handles are drawn only during repaint events and not on selection.
|
||||
/// </summary>
|
||||
/// <param name="knot">The <see cref="SelectableKnot"/> to create handles for.</param>
|
||||
/// <param name="selected">Set to true to draw the knot handle as a selected element.</param>
|
||||
/// <param name="hovered">Set to true to draw the knot handle as a hovered element.</param>
|
||||
public static void DrawKnot(SelectableKnot knot, bool selected = false, bool hovered = false)
|
||||
=> DrawKnot(-1, knot, selected, hovered);
|
||||
|
||||
/// <summary>
|
||||
/// Draws handles for a knot. These handles are drawn only during repaint events and not on selection.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The controlID of the tangent to create handles for.</param>
|
||||
/// <param name="knot">The <see cref="SelectableKnot"/> to create handles for.</param>
|
||||
/// <param name="selected">Set to true to draw the knot handle as a selected element.</param>
|
||||
/// <param name="hovered">Set to true to draw the knot handle as a hovered element.</param>
|
||||
public static void DrawKnot(int controlID, SelectableKnot knot, bool selected = false, bool hovered = false)
|
||||
{
|
||||
KnotHandles.Do(controlID, knot, selected, hovered);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws handles for a tangent. These handles are drawn only during repaint events and not on selection.
|
||||
/// </summary>
|
||||
/// <param name="tangent">The <see cref="SelectableTangent"/> to create handles for.</param>
|
||||
/// <param name="selected">Set to true to draw the tangent handle as a selected element.</param>
|
||||
/// <param name="hovered">Set to true to draw the tangent handle as a hovered element.</param>
|
||||
public static void DrawTangent(SelectableTangent tangent, bool selected = false, bool hovered = false) => DrawTangent(-1, tangent, selected, hovered);
|
||||
|
||||
/// <summary>
|
||||
/// Draws handles for a tangent. These handles are drawn only during repaint events and not on selection.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The controlID of the tangent to create handles for.</param>
|
||||
/// <param name="tangent">The <see cref="SelectableTangent"/> to create handles for.</param>
|
||||
/// <param name="selected">Set to true to draw the tangent handle as a selected element.</param>
|
||||
/// <param name="hovered">Set to true to draw the tangent handle as a hovered element.</param>
|
||||
public static void DrawTangent(int controlID, SelectableTangent tangent, bool selected = false, bool hovered = false)
|
||||
{
|
||||
TangentHandles.Do(controlID, tangent, selected, hovered);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa11edcb768491b4b8b90ccf074e663f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a cylinder mesh along a spline.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of ISpline.</typeparam>
|
||||
public class SplineMeshHandle<T> : IDisposable where T : ISpline
|
||||
{
|
||||
class SplineMeshDrawingScope : IDisposable
|
||||
{
|
||||
Material m_Material;
|
||||
|
||||
int m_HandleZTestId;
|
||||
int m_BlendSrcModeId;
|
||||
int m_BlendDstModeId;
|
||||
|
||||
float m_PreviousZTest;
|
||||
int m_PreviousBlendSrcMode;
|
||||
int m_PreviousBlendDstMode;
|
||||
|
||||
public SplineMeshDrawingScope(Material material, Color color)
|
||||
{
|
||||
Shader.SetGlobalColor("_HandleColor", color);
|
||||
Shader.SetGlobalFloat("_HandleSize", 1f);
|
||||
Shader.SetGlobalMatrix("_ObjectToWorld", Handles.matrix);
|
||||
|
||||
if (material == null)
|
||||
{
|
||||
m_Material = HandleUtility.handleMaterial;
|
||||
|
||||
m_HandleZTestId = Shader.PropertyToID("_HandleZTest");
|
||||
m_BlendSrcModeId = Shader.PropertyToID("_BlendSrcMode");
|
||||
m_BlendDstModeId = Shader.PropertyToID("_BlendDstMode");
|
||||
|
||||
m_PreviousZTest = m_Material.GetFloat(m_HandleZTestId);
|
||||
m_PreviousBlendSrcMode = m_Material.GetInt(m_BlendSrcModeId);
|
||||
m_PreviousBlendDstMode = m_Material.GetInt(m_BlendDstModeId);
|
||||
|
||||
m_Material.SetFloat(m_HandleZTestId, (float)Handles.zTest);
|
||||
m_Material.SetInt(m_BlendSrcModeId, (int)UnityEngine.Rendering.BlendMode.One);
|
||||
m_Material.SetInt(m_BlendDstModeId, (int)UnityEngine.Rendering.BlendMode.One);
|
||||
|
||||
m_Material.SetPass(0);
|
||||
}
|
||||
else
|
||||
material.SetPass(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Material != null)
|
||||
{
|
||||
m_Material.SetFloat(m_HandleZTestId, m_PreviousZTest);
|
||||
m_Material.SetInt(m_BlendSrcModeId, m_PreviousBlendSrcMode);
|
||||
m_Material.SetInt(m_BlendDstModeId, m_PreviousBlendDstMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mesh m_Mesh;
|
||||
|
||||
Material m_Material;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new mesh handle. This class implements IDisposable to clean up allocated mesh resources. Call
|
||||
/// <see cref="Dispose"/> when you are finished with the instance.
|
||||
/// </summary>
|
||||
public SplineMeshHandle()
|
||||
{
|
||||
m_Mesh = new Mesh()
|
||||
{
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
|
||||
m_Material = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new mesh handle. This class implements IDisposable to clean up allocated mesh resources. Call
|
||||
/// <see cref="Dispose"/> when you are finished with the instance.
|
||||
/// </summary>
|
||||
/// <param name="material">The material to render the cylinder mesh with.</param>
|
||||
public SplineMeshHandle(Material material) : base()
|
||||
{
|
||||
m_Material = material;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The material to render this mesh with. If null, a default material is used.
|
||||
/// </summary>
|
||||
public Material material
|
||||
{
|
||||
get => m_Material;
|
||||
set => m_Material = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a 3D mesh from a spline.
|
||||
/// </summary>
|
||||
/// <param name="spline">The target spline.</param>
|
||||
/// <param name="size">The width to use for the spline mesh.</param>
|
||||
/// <param name="color">The color to use for the spline mesh in normal mode.</param>
|
||||
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
|
||||
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
|
||||
public void Do(T spline, float size, Color color, int resolution = SplineUtility.DrawResolutionDefault)
|
||||
{
|
||||
if(Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
Do(-1, spline, size, color, resolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a 3D mesh handle from a spline.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The spline mesh controlID.</param>
|
||||
/// <param name="spline">The target spline.</param>
|
||||
/// <param name="size">The width to use for the spline mesh.</param>
|
||||
/// <param name="color">The color to use for the spline mesh in normal mode.</param>
|
||||
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
|
||||
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
|
||||
public void Do(int controlID, T spline, float size, Color color, int resolution = SplineUtility.DrawResolutionDefault)
|
||||
{
|
||||
using (new Handles.DrawingScope(color))
|
||||
Do(controlID, spline, size, resolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a 3D mesh from a spline.
|
||||
/// </summary>
|
||||
/// <param name="spline">The target spline.</param>
|
||||
/// <param name="size">The width to use for the spline mesh.</param>
|
||||
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
|
||||
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
|
||||
public void Do(T spline, float size, int resolution = SplineUtility.DrawResolutionDefault)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
Do(-1, spline, size, resolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a 3D mesh handle from a spline.
|
||||
/// </summary>
|
||||
/// <param name="controlID">The spline mesh controlID.</param>
|
||||
/// <param name="spline">The target spline.</param>
|
||||
/// <param name="size">The width to use for the spline mesh.</param>
|
||||
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
|
||||
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
|
||||
public void Do(int controlID, T spline, float size, int resolution = SplineUtility.DrawResolutionDefault)
|
||||
{
|
||||
var evt = Event.current;
|
||||
|
||||
switch (evt.type)
|
||||
{
|
||||
case EventType.MouseMove:
|
||||
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
|
||||
HandleUtility.AddControl(controlID, SplineUtility.GetNearestPoint(spline, ray, out _, out _));
|
||||
break;
|
||||
|
||||
case EventType.Repaint:
|
||||
var segments = SplineUtility.GetSubdivisionCount(spline.GetLength(), resolution);
|
||||
SplineMesh.Extrude(spline, m_Mesh, size, 8, segments, !spline.Closed);
|
||||
var color = GUIUtility.hotControl == controlID
|
||||
? Handles.selectedColor
|
||||
: HandleUtility.nearestControl == controlID
|
||||
? Handles.preselectionColor
|
||||
: Handles.color;
|
||||
using (new SplineMeshDrawingScope(m_Material, color))
|
||||
Graphics.DrawMeshNow(m_Mesh, Handles.matrix);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the 3D mesh.
|
||||
/// </summary>
|
||||
public void Dispose() => Object.DestroyImmediate(m_Mesh);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7822ae247afa5945befac0bc63be0bb
|
||||
timeCreated: 1654094147
|
||||
@@ -0,0 +1,238 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class TangentHandles
|
||||
{
|
||||
const float k_ColorAlphaFactor = 0.3f;
|
||||
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_TangentHandleWidth = 2f;
|
||||
const float k_TangentRotWidthDefault = 1.5f;
|
||||
const float k_TangentRotDiscWidth = 3f;
|
||||
|
||||
internal static void Do(int controlId, SelectableTangent tangent, bool selected = false, bool hovered = false)
|
||||
{
|
||||
var owner = tangent.Owner;
|
||||
Draw(
|
||||
controlId,
|
||||
tangent.Position,
|
||||
EditorSplineUtility.GetElementRotation(math.length(tangent.LocalPosition) > 0 ? (ISelectableElement)tangent : tangent.Owner),
|
||||
owner.Position,
|
||||
selected,
|
||||
false,
|
||||
hovered,
|
||||
false,
|
||||
owner.Mode,
|
||||
true);
|
||||
}
|
||||
|
||||
internal static void DrawInformativeTangent(SelectableTangent tangent, bool active = true)
|
||||
{
|
||||
DrawInformativeTangent(tangent.Position, tangent.Owner.Position, active);
|
||||
}
|
||||
|
||||
internal static void DrawInformativeTangent(Vector3 position, Vector3 knotPosition, bool active = true)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
var tangentColor = SplineHandleUtility.elementColor;
|
||||
if (!active)
|
||||
tangentColor = Handles.secondaryColor;
|
||||
|
||||
var tangentArmColor = tangentColor == SplineHandleUtility.elementColor
|
||||
? SplineHandleUtility.tangentColor
|
||||
: tangentColor;
|
||||
|
||||
using (new ColorScope(tangentArmColor))
|
||||
{
|
||||
var toTangent = position - knotPosition;
|
||||
var toTangentNorm = math.normalize(toTangent);
|
||||
var length = math.length(toTangent);
|
||||
var knotHandleOffset = HandleUtility.GetHandleSize(knotPosition) * k_TangentStartOffsetFromKnot;
|
||||
|
||||
length = Mathf.Max(0f, length - knotHandleOffset);
|
||||
knotPosition += (Vector3)toTangentNorm * knotHandleOffset;
|
||||
|
||||
SplineHandleUtility.DrawLineWithWidth(knotPosition, knotPosition + (Vector3)toTangentNorm * length, k_TangentLineWidthDefault);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Draw(Vector3 position, Vector3 knotPosition, float3 normal, bool active = true)
|
||||
{
|
||||
var knotToTangentDirection = position - knotPosition;
|
||||
var rotation = quaternion.LookRotationSafe(knotToTangentDirection, normal);
|
||||
Draw(-1, position, rotation, knotPosition, false, false, false, TangentMode.Broken, active);
|
||||
}
|
||||
|
||||
internal static void Draw(int controlId, SelectableTangent tangent, bool active = true)
|
||||
{
|
||||
var (pos, rot) = SplineCacheUtility.GetTangentPositionAndRotation(tangent);
|
||||
var owner = tangent.Owner;
|
||||
Draw(
|
||||
controlId,
|
||||
pos,
|
||||
rot,
|
||||
owner.Position,
|
||||
SplineSelection.Contains(tangent),
|
||||
SplineSelection.Contains(tangent.OppositeTangent),
|
||||
SplineHandleUtility.IsLastHoveredElement(tangent.OppositeTangent),
|
||||
owner.Mode,
|
||||
active);
|
||||
}
|
||||
|
||||
static void Draw(int controlId, Vector3 position, Quaternion rotation, Vector3 knotPosition, bool selected, bool oppositeSelected, bool oppositeHovered, TangentMode mode, bool active)
|
||||
{
|
||||
var hovered = SplineHandleUtility.IsHoverAvailableForSplineElement() && SplineHandleUtility.IsElementHovered(controlId);
|
||||
Draw(controlId, position, rotation, knotPosition, selected, oppositeSelected, hovered, oppositeHovered, mode, active);
|
||||
}
|
||||
|
||||
static void Draw(int controlId, Vector3 position, Quaternion rotation, Vector3 knotPosition, bool selected, bool oppositeSelected, bool hovered, bool oppositeHovered, TangentMode mode, bool active)
|
||||
{
|
||||
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
|
||||
return;
|
||||
|
||||
var size = HandleUtility.GetHandleSize(position);
|
||||
|
||||
var tangentColor = SplineHandleUtility.elementColor;
|
||||
if (hovered)
|
||||
tangentColor = SplineHandleUtility.elementPreselectionColor;
|
||||
else if (selected)
|
||||
tangentColor = SplineHandleUtility.elementSelectionColor;
|
||||
|
||||
if (!active)
|
||||
tangentColor = Handles.secondaryColor;
|
||||
|
||||
var tangentArmColor = tangentColor == SplineHandleUtility.elementColor ?
|
||||
SplineHandleUtility.tangentColor :
|
||||
tangentColor;
|
||||
|
||||
if (mode == TangentMode.Mirrored)
|
||||
{
|
||||
if(oppositeHovered)
|
||||
tangentArmColor = SplineHandleUtility.elementPreselectionColor;
|
||||
else if(tangentArmColor == SplineHandleUtility.tangentColor && oppositeSelected)
|
||||
tangentArmColor =SplineHandleUtility.elementSelectionColor;
|
||||
}
|
||||
|
||||
var rotationDiscWidth = k_TangentRotWidthDefault;
|
||||
if (hovered)
|
||||
rotationDiscWidth = k_TangentRotDiscWidth;
|
||||
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
{
|
||||
// Draw tangent arm.
|
||||
using (new ColorScope(tangentArmColor))
|
||||
DrawTangentArm(position, knotPosition, size, mode, selected, hovered, oppositeSelected, oppositeHovered);
|
||||
|
||||
// Draw tangent shape.
|
||||
using (new Handles.DrawingScope(tangentColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
DrawTangentShape(size, selected);
|
||||
}
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
{
|
||||
// Draw tangent arm.
|
||||
var newTangentArmColor = new Color(tangentArmColor.r, tangentArmColor.g, tangentArmColor.b, tangentArmColor.a * k_ColorAlphaFactor);
|
||||
using (new ColorScope(newTangentArmColor))
|
||||
DrawTangentArm(position, knotPosition, size, mode, selected, hovered, oppositeSelected, oppositeHovered);
|
||||
|
||||
// Draw tangent shape.
|
||||
var newDiscColor = new Color(tangentColor.r, tangentColor.g, tangentColor.b, tangentColor.a * k_ColorAlphaFactor);
|
||||
using (new Handles.DrawingScope(newDiscColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
DrawTangentShape(size, selected);
|
||||
}
|
||||
|
||||
// Draw tangent disc on hover.
|
||||
if (hovered)
|
||||
{
|
||||
var tangentHandleOffset = size * k_TangentEndOffsetFromHandle;
|
||||
using (new ZTestScope(CompareFunction.Less))
|
||||
{
|
||||
using (new Handles.DrawingScope(tangentColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, tangentHandleOffset, rotationDiscWidth);
|
||||
}
|
||||
|
||||
using (new ZTestScope(CompareFunction.Greater))
|
||||
{
|
||||
var newDiscColor = new Color(tangentColor.r, tangentColor.g, tangentColor.b, tangentColor.a * k_ColorAlphaFactor);
|
||||
using (new Handles.DrawingScope(newDiscColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
|
||||
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, tangentHandleOffset, rotationDiscWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawTangentArm(Vector3 position, Vector3 knotPosition, float size, TangentMode mode, bool selected, bool hovered, bool oppositeSelected, bool oppositeHovered)
|
||||
{
|
||||
var width = k_TangentLineWidthDefault;
|
||||
if (!DirectManipulation.IsDragging)
|
||||
{
|
||||
if (selected || (mode != TangentMode.Broken && oppositeSelected))
|
||||
width = k_TangentLineWidthSelected;
|
||||
else if (hovered || (mode != TangentMode.Broken && oppositeHovered))
|
||||
width = k_TangentLineWidthHover;
|
||||
}
|
||||
|
||||
var startPos = knotPosition;
|
||||
var toTangent = position - knotPosition;
|
||||
var toTangentNorm = math.normalize(toTangent);
|
||||
var length = math.length(toTangent);
|
||||
|
||||
var knotHandleSize = HandleUtility.GetHandleSize(startPos);
|
||||
var knotHandleOffset = knotHandleSize * k_TangentStartOffsetFromKnot;
|
||||
var tangentHandleOffset = size * 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 += (Vector3)toTangentNorm * knotHandleOffset;
|
||||
|
||||
SplineHandleUtility.DrawLineWithWidth(startPos + (Vector3)toTangentNorm * length, startPos, width, SplineHandleUtility.denseLineAATex);
|
||||
}
|
||||
|
||||
static void DrawTangentShape(float size, bool selected)
|
||||
{
|
||||
var midVector = new Vector3(-.5f, 0, .5f);
|
||||
if (selected)
|
||||
{
|
||||
var factor = 0.7f;
|
||||
var radius = (selected ? SplineHandleUtility.knotDiscRadiusFactorSelected : SplineHandleUtility.knotDiscRadiusFactorHover) * size;
|
||||
// As Handles.DrawAAConvexPolygon has no thickness parameter, we're drawing a AA Polyline here so that the polygon has thickness when viewed from a shallow angle.
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex,
|
||||
k_TangentHandleWidth,
|
||||
factor * radius * midVector,
|
||||
factor * radius * Vector3.forward,
|
||||
factor * radius * Vector3.right,
|
||||
-factor * radius * Vector3.forward,
|
||||
-factor * radius * Vector3.right,
|
||||
factor * radius * midVector);
|
||||
Handles.DrawAAConvexPolygon(
|
||||
radius * midVector,
|
||||
radius * Vector3.forward,
|
||||
radius * Vector3.right,
|
||||
-radius * Vector3.forward,
|
||||
-radius * Vector3.right,
|
||||
radius * midVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
var radius = SplineHandleUtility.knotDiscRadiusFactorDefault * size;
|
||||
//Starting the polyline in the middle of a segment and not to a corner to get an invisible connection.
|
||||
//Otherwise the connection is really visible in the corner as a small part is missing there.
|
||||
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex,
|
||||
k_TangentHandleWidth,
|
||||
radius * midVector,
|
||||
radius * Vector3.forward,
|
||||
radius * Vector3.right,
|
||||
-radius * Vector3.forward,
|
||||
-radius * Vector3.right,
|
||||
radius * midVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8805f50579982e7458c1b5ecc0119396
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user