initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class EditableSplineUtility
|
||||
{
|
||||
public static SplineType GetSplineType(IEditableSpline spline)
|
||||
{
|
||||
switch (spline)
|
||||
{
|
||||
case BezierEditableSpline _:
|
||||
return SplineType.Bezier;
|
||||
case CatmullRomEditableSpline _:
|
||||
return SplineType.CatmullRom;
|
||||
case LinearEditableSpline _:
|
||||
return SplineType.Linear;
|
||||
default:
|
||||
throw new ArgumentException(nameof(spline));
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEditableSpline CreatePathOfType(SplineType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SplineType.Bezier: return new BezierEditableSpline();
|
||||
case SplineType.CatmullRom: return new CatmullRomEditableSpline();
|
||||
case SplineType.Linear: return new LinearEditableSpline();
|
||||
default:
|
||||
throw new InvalidEnumArgumentException(nameof(type));
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetSelectedSplines(IEnumerable<Object> targets, List<IEditableSpline> results)
|
||||
{
|
||||
results.Clear();
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var splines = GetSelectedSpline(target);
|
||||
if (splines == null)
|
||||
continue;
|
||||
|
||||
results.AddRange(splines);
|
||||
}
|
||||
}
|
||||
|
||||
public static IReadOnlyList<IEditableSpline> GetSelectedSpline(Object target)
|
||||
{
|
||||
return EditableSplineManager.GetEditableSplines(target);
|
||||
}
|
||||
|
||||
internal static Bounds GetBounds(IReadOnlyList<ISplineElement> elements,
|
||||
bool useKnotPositionForTangents = false)
|
||||
{
|
||||
if (elements == null)
|
||||
throw new ArgumentNullException(nameof(elements));
|
||||
|
||||
if (elements.Count == 0)
|
||||
return new Bounds(Vector3.positiveInfinity, Vector3.zero);
|
||||
|
||||
var element = elements[0];
|
||||
|
||||
var position = (useKnotPositionForTangents && element is EditableTangent)?
|
||||
((EditableTangent)element).owner.position :
|
||||
element.position;
|
||||
|
||||
Bounds bounds = new Bounds(position, Vector3.zero);
|
||||
for (int i = 1; i < elements.Count; ++i)
|
||||
{
|
||||
element = elements[i];
|
||||
if(useKnotPositionForTangents && element is EditableTangent tangent)
|
||||
bounds.Encapsulate(tangent.owner.position);
|
||||
else
|
||||
bounds.Encapsulate(element.position);
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public static EditableKnot InsertKnotOnCurve(CurveData curve, Vector3 position, float t)
|
||||
{
|
||||
var path = curve.a.spline;
|
||||
var prev = curve.a;
|
||||
var next = curve.b;
|
||||
|
||||
EditableKnot knot = path.InsertKnot(next.index);
|
||||
knot.position = position;
|
||||
knot.OnKnotInsertedOnCurve(prev, next, t);
|
||||
|
||||
return knot;
|
||||
}
|
||||
|
||||
public static void AddPointToEnd(IEditableSpline spline, Vector3 worldPosition, Vector3 normal, Vector3 tangentOut)
|
||||
{
|
||||
if (spline.closed)
|
||||
throw new ArgumentException("Cannot add a point to the end of a closed spline", nameof(spline));
|
||||
|
||||
EditableKnot knot = spline.AddKnot();
|
||||
knot.position = worldPosition;
|
||||
spline.OnKnotAddedAtEnd(knot, normal, tangentOut);
|
||||
}
|
||||
|
||||
public static void CloseSpline(IEditableSpline spline)
|
||||
{
|
||||
if (spline.knotCount <= 1 && !spline.canBeClosed)
|
||||
return;
|
||||
|
||||
spline.closed = true;
|
||||
}
|
||||
|
||||
internal static float3 ToSplineSpaceTangent(this EditableKnot knot, float3 knotSpaceTangent)
|
||||
{
|
||||
return math.rotate(knot.localRotation, knotSpaceTangent);
|
||||
}
|
||||
|
||||
internal static float3 ToKnotSpaceTangent(this EditableKnot knot, float3 splineSpaceTangent)
|
||||
{
|
||||
return math.rotate(math.inverse(knot.localRotation), splineSpaceTangent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef711f087c2a48944a4c5792faa50378
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
/// <summary>
|
||||
/// Editor utility functions for working with <see cref="Spline"/> and <see cref="SplineData{T}"/>.
|
||||
/// </summary>
|
||||
public static class EditorSplineUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked once per-frame if a spline property has been modified.
|
||||
/// </summary>
|
||||
public static event Action<Spline> afterSplineWasModified;
|
||||
|
||||
static EditorSplineUtility()
|
||||
{
|
||||
Spline.afterSplineWasModified += (spline) =>
|
||||
{
|
||||
afterSplineWasModified?.Invoke(spline);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this function to register a callback that gets invoked
|
||||
/// once per-frame if any <see cref="SplineData{T}"/> changes occur.
|
||||
/// </summary>
|
||||
/// <param name="action">The callback to register.</param>
|
||||
/// <typeparam name="T">
|
||||
/// The type parameter of <see cref="SplineData{T}"/>.
|
||||
/// </typeparam>
|
||||
public static void RegisterSplineDataChanged<T>(Action<SplineData<T>> action)
|
||||
{
|
||||
SplineData<T>.afterSplineDataWasModified += action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this function to unregister <see cref="SplineData{T}"/> change callback.
|
||||
/// </summary>
|
||||
/// <param name="action">The callback to unregister.</param>
|
||||
/// <typeparam name="T">
|
||||
/// The type parameter of <see cref="SplineData{T}"/>.
|
||||
/// </typeparam>
|
||||
public static void UnregisterSplineDataChanged<T>(Action<SplineData<T>> action)
|
||||
{
|
||||
SplineData<T>.afterSplineDataWasModified -= action;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e7363f1a86c8d4620b2e20ff9a768e6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,19 @@
|
|||
using UnityEngine;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class MathUtility
|
||||
{
|
||||
public static float GetRollAroundAxis(quaternion q, float3 axis)
|
||||
{
|
||||
axis = math.normalize(axis);
|
||||
var orthoAxis = math.cross(axis, axis.Equals(math.up()) ? math.right() : math.up());
|
||||
var rotatedOrtho = math.rotate(q, orthoAxis);
|
||||
var flattened = math.normalize(Vector3.ProjectOnPlane(rotatedOrtho, axis));
|
||||
var angle = Mathf.Deg2Rad * Vector3.SignedAngle(orthoAxis, flattened, axis);
|
||||
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae14811f375884af48729e154f600562
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,31 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class PathIcons
|
||||
{
|
||||
public static GUIContent knotPlacementTool = EditorGUIUtility.TrIconContent(GetIcon("KnotPlacementTool"), "Knot Placement Tool");
|
||||
public static GUIContent splineMoveTool = EditorGUIUtility.TrIconContent("MoveTool", "Spline Move Tool");
|
||||
public static GUIContent splineRotateTool = EditorGUIUtility.TrIconContent("RotateTool", "Spline Rotate Tool");
|
||||
public static GUIContent splineScaleTool = EditorGUIUtility.TrIconContent("ScaleTool", "Spline Scale Tool");
|
||||
|
||||
static Texture2D GetIcon(string name)
|
||||
{
|
||||
bool is2x = EditorGUIUtility.pixelsPerPoint > 1;
|
||||
bool darkSkin = EditorGUIUtility.isProSkin;
|
||||
string path = string.Format($"Icons/{(darkSkin ? "d_" : "")}{name}{(is2x ? "@2x" : "")}");
|
||||
|
||||
Texture2D texture = Resources.Load<Texture2D>(path);
|
||||
if (texture != null)
|
||||
return texture;
|
||||
|
||||
path = string.Format($"Icons/{(darkSkin ? "d_" : "")}{name}");
|
||||
texture = Resources.Load<Texture2D>(path);
|
||||
if (texture != null)
|
||||
return texture;
|
||||
|
||||
path = string.Format($"Icons/{name}");
|
||||
return Resources.Load<Texture2D>(path);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f7fd3d3d53b474642bd4aeaf58564b69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,59 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class SplineCacheUtility
|
||||
{
|
||||
const int k_SegmentsCount = 32;
|
||||
|
||||
static Dictionary<Spline, Vector3[]> s_SplineCacheTable = new Dictionary<Spline, Vector3[]>();
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
static void Initialize()
|
||||
{
|
||||
EditorSplineUtility.afterSplineWasModified += delegate(Spline spline)
|
||||
{
|
||||
if(s_SplineCacheTable.ContainsKey(spline))
|
||||
s_SplineCacheTable[spline] = null;
|
||||
};
|
||||
|
||||
Spline.afterSplineWasModified += (Spline s) => ClearCache();
|
||||
Undo.undoRedoPerformed += ClearCache;
|
||||
SplineConversionUtility.splinesUpdated += ClearCache;
|
||||
}
|
||||
|
||||
static void ClearCache()
|
||||
{
|
||||
s_SplineCacheTable.Clear();
|
||||
}
|
||||
|
||||
public static void GetCachedPositions(Spline spline, out Vector3[] positions)
|
||||
{
|
||||
if(!s_SplineCacheTable.ContainsKey(spline))
|
||||
s_SplineCacheTable.Add(spline, null);
|
||||
|
||||
int count = spline.Closed ? spline.Count : spline.Count - 1;
|
||||
|
||||
if(s_SplineCacheTable[spline] == null)
|
||||
{
|
||||
s_SplineCacheTable[spline] = new Vector3[count * k_SegmentsCount];
|
||||
CacheCurvePositionsTable(spline, count);
|
||||
}
|
||||
positions = s_SplineCacheTable[spline];
|
||||
}
|
||||
|
||||
static void CacheCurvePositionsTable(Spline spline, int curveCount, int segments = k_SegmentsCount)
|
||||
{
|
||||
float inv = 1f / (segments - 1);
|
||||
for(int i = 0; i < curveCount; ++i)
|
||||
{
|
||||
var curve = spline.GetCurve(i);
|
||||
var startIndex = i * k_SegmentsCount;
|
||||
for(int n = 0; n < segments; n++)
|
||||
(s_SplineCacheTable[spline])[startIndex + n] = CurveUtility.EvaluatePosition(curve, n * inv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9fd9a2b5315b4e498978bab7499f95ac
|
||||
timeCreated: 1646151455
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using UObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class SplineConversionUtility
|
||||
{
|
||||
internal static Action splinesUpdated;
|
||||
internal static Action splineTypeChanged;
|
||||
|
||||
static readonly List<UObject> s_TargetsBuffer = new List<UObject>();
|
||||
static readonly List<BezierKnot> s_KnotBuffer = new List<BezierKnot>();
|
||||
|
||||
public static void UpdateEditableSplinesForTargetType<T>() where T : UObject
|
||||
{
|
||||
UpdateEditableSplinesForTargetType(typeof(T));
|
||||
}
|
||||
|
||||
public static void UpdateEditableSplinesForTargetType(Type type)
|
||||
{
|
||||
EditableSplineManager.GetTargetsOfType(type, s_TargetsBuffer);
|
||||
UpdateEditableSplinesForTargets(s_TargetsBuffer);
|
||||
}
|
||||
|
||||
public static void UpdateEditableSplinesForTargets(IEnumerable<UObject> target)
|
||||
{
|
||||
foreach (var owner in target)
|
||||
{
|
||||
UpdateEditableSplinesInternal(owner, true);
|
||||
}
|
||||
|
||||
splinesUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public static void UpdateEditableSplinesForTarget(UObject target)
|
||||
{
|
||||
UpdateEditableSplinesInternal(target, false);
|
||||
}
|
||||
|
||||
static void UpdateEditableSplinesInternal(UObject target, bool silent)
|
||||
{
|
||||
//Update IEditableSpline from its Spline target
|
||||
if (!EditableSplineManager.TryGetTargetData(target, out EditableSplineManager.TargetData data))
|
||||
return;
|
||||
|
||||
if (data.RawSplines.Count == 0)
|
||||
return;
|
||||
|
||||
var provider = (ISplineProvider) target;
|
||||
data.RawSplines.Clear();
|
||||
data.RawSplines.AddRange(provider.Splines);
|
||||
|
||||
//Ensure same number of editable splines than spline contained on the target
|
||||
Array.Resize(ref data.EditableSplines, data.RawSplines.Count);
|
||||
|
||||
bool typeChanged = false;
|
||||
for (var i = 0; i < data.RawSplines.Count; ++i)
|
||||
{
|
||||
var spline = data.RawSplines[i];
|
||||
var editableSpline = data.EditableSplines[i];
|
||||
|
||||
if (editableSpline == null || EditableSplineUtility.GetSplineType(editableSpline) != spline.EditType)
|
||||
{
|
||||
var newPath = EditableSplineUtility.CreatePathOfType(spline.EditType);
|
||||
newPath.conversionIndex = i;
|
||||
newPath.conversionTarget = target;
|
||||
|
||||
data.EditableSplines[i] = newPath;
|
||||
editableSpline = newPath;
|
||||
|
||||
typeChanged = true;
|
||||
}
|
||||
|
||||
editableSpline.closed = spline.Closed;
|
||||
s_KnotBuffer.Clear();
|
||||
for (int j = 0; j < spline.Count; ++j)
|
||||
{
|
||||
s_KnotBuffer.Add(spline[j]);
|
||||
}
|
||||
|
||||
editableSpline.FromBezier(s_KnotBuffer);
|
||||
|
||||
//Remove dirty flags to not reapply back to target
|
||||
editableSpline.isDirty = false;
|
||||
editableSpline.ValidateData();
|
||||
}
|
||||
|
||||
if (typeChanged)
|
||||
splineTypeChanged?.Invoke();
|
||||
|
||||
if (!silent)
|
||||
splinesUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public static bool ApplyEditableSplinesOfTypeIfDirty<T>() where T : UObject
|
||||
{
|
||||
return ApplyEditableSplinesOfTypeIfDirty(typeof(T));
|
||||
}
|
||||
|
||||
public static bool ApplyEditableSplinesOfTypeIfDirty(Type type)
|
||||
{
|
||||
EditableSplineManager.GetTargetsOfType(type, s_TargetsBuffer);
|
||||
return ApplyEditableSplinesIfDirty(s_TargetsBuffer);
|
||||
}
|
||||
|
||||
public static bool ApplyEditableSplinesIfDirty(IEnumerable<UObject> owners)
|
||||
{
|
||||
bool result = false;
|
||||
foreach (var owner in owners)
|
||||
{
|
||||
result |= ApplyEditableSplinesIfDirty(owner);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool ApplyEditableSplinesIfDirty(UObject target)
|
||||
{
|
||||
if (!EditableSplineManager.TryGetTargetData(target, out EditableSplineManager.TargetData data))
|
||||
return false;
|
||||
|
||||
if (!IsAnyEditableSplineDirty(data.EditableSplines))
|
||||
return false;
|
||||
|
||||
Undo.RecordObject(target, "Apply Changes to Spline");
|
||||
|
||||
for (var i = 0; i < data.EditableSplines.Length; ++i)
|
||||
{
|
||||
var editablePath = data.EditableSplines[i];
|
||||
var targetSpline = data.RawSplines[i];
|
||||
|
||||
if (editablePath.isDirty)
|
||||
{
|
||||
editablePath.isDirty = false;
|
||||
targetSpline.Closed = editablePath.closed;
|
||||
s_KnotBuffer.Clear();
|
||||
editablePath.ToBezier(s_KnotBuffer);
|
||||
|
||||
targetSpline.Resize(s_KnotBuffer.Count);
|
||||
for (int j = 0; j < s_KnotBuffer.Count; ++j)
|
||||
{
|
||||
targetSpline[j] = s_KnotBuffer[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsAnyEditableSplineDirty(IEnumerable<IEditableSpline> paths)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (path.isDirty)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ed4b96ba9e5c69c4dae87773bfec943c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Internal;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
[ExcludeFromDocs]
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class CustomSplineDataHandle : Attribute
|
||||
{
|
||||
internal Type m_Type;
|
||||
|
||||
// Tells a PropertyDrawer class which run-time class or attribute it's a drawer for.
|
||||
public CustomSplineDataHandle(Type type)
|
||||
{
|
||||
m_Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
interface ISplineDataHandle
|
||||
{
|
||||
public SplineDataHandleAttribute attribute
|
||||
{ get; }
|
||||
|
||||
void SetAttribute(SplineDataHandleAttribute attribute);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SplineDataHandle is a base class to override in order to enable custom handles for spline data.
|
||||
/// The Drawer needs to inherit from this class and override the method corresponding to the correct splineData type.
|
||||
/// Either one of the method or both can be overriden regarding the user needs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type parameter of the <see cref="SplineData{T}"/> that this drawer targets.
|
||||
/// </typeparam>
|
||||
public abstract class SplineDataHandle<T> : ISplineDataHandle
|
||||
{
|
||||
internal int[] m_ControlIDs;
|
||||
|
||||
SplineDataHandleAttribute m_Attribute;
|
||||
|
||||
[ExcludeFromDocs]
|
||||
public SplineDataHandleAttribute attribute => m_Attribute;
|
||||
|
||||
/// <summary>
|
||||
/// Array of reserved control IDs used for <see cref="SplineData{T}"/> handles.
|
||||
/// </summary>
|
||||
public int[] controlIDs => m_ControlIDs;
|
||||
|
||||
void ISplineDataHandle.SetAttribute(SplineDataHandleAttribute attribute)
|
||||
{
|
||||
m_Attribute = attribute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to create custom handles for <see cref="SplineData{T}"/>,
|
||||
/// this method is called before DrawKeyframe in the render loop.
|
||||
/// </summary>
|
||||
/// <param name="splineData">The <see cref="SplineData{T}"/> for which the method is drawing handles.</param>
|
||||
/// <param name="spline">The target Spline associated to the SplineData for the drawing.</param>
|
||||
/// <param name="localToWorld">The spline localToWorld Matrix.</param>
|
||||
/// <param name="color">The color defined in the SplineData scene interface.</param>
|
||||
public virtual void DrawSplineData(SplineData<T> splineData, Spline spline, Matrix4x4 localToWorld, Color color)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to create custom handles for a <see cref="DataPoint{T}"/>in <see cref="SplineData{T}"/>,
|
||||
/// 'position' and 'direction' are given in the Spline-space basis.
|
||||
/// This method is called after DrawSplineData in the render loop.
|
||||
/// </summary>
|
||||
/// <param name="controlID">A control ID from <see cref="controlIDs"/> that represents this handle.</param>
|
||||
/// <param name="position">The position of the keyframe data in spline space.</param>
|
||||
/// <param name="direction">The direction of the spline at the current keyframe.</param>
|
||||
/// <param name="upDirection">The up vector orthogonal to the spline direction at the current keyframe regarding knot rotation.</param>
|
||||
/// <param name="splineData">The <see cref="SplineData{T}"/> for which the method is drawing handles.</param>
|
||||
/// <param name="dataPointIndex">The index of the current keyframe to handle.</param>
|
||||
public virtual void DrawDataPoint(
|
||||
int controlID,
|
||||
Vector3 position,
|
||||
Vector3 direction,
|
||||
Vector3 upDirection,
|
||||
SplineData<T> splineData,
|
||||
int dataPointIndex)
|
||||
{}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2495f958516e4867aa9cbcbcf916836c
|
||||
timeCreated: 1625521293
|
|
@ -0,0 +1,387 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.SettingsManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
static class SplineDataHandlesManager //: ScriptableSingleton<SplineDataHandlesManager>
|
||||
{
|
||||
// Using by default colors of maximal contrast from
|
||||
// https://medium.com/@rjurney/kellys-22-colours-of-maximum-contrast-58edb70c90d1
|
||||
static UserSetting<Color> s_Color0 = new UserSetting<Color>(PathSettings.instance, "SplineData.color0", new Color32(0, 130, 200, 255), SettingsScope.User); // Blue
|
||||
static UserSetting<Color> s_Color1 = new UserSetting<Color>(PathSettings.instance, "SplineData.color1", new Color32(245, 130, 48, 255), SettingsScope.User); // Orange
|
||||
static UserSetting<Color> s_Color2 = new UserSetting<Color>(PathSettings.instance, "SplineData.color2", new Color32(60, 180, 75, 255), SettingsScope.User); // Green
|
||||
static UserSetting<Color> s_Color3 = new UserSetting<Color>(PathSettings.instance, "SplineData.color3", new Color32(255, 225, 25, 255), SettingsScope.User); // Yellow
|
||||
static UserSetting<Color> s_Color4 = new UserSetting<Color>(PathSettings.instance, "SplineData.color4", new Color32(145, 30, 180, 255), SettingsScope.User); // Purple
|
||||
static UserSetting<Color> s_Color5 = new UserSetting<Color>(PathSettings.instance, "SplineData.color5", new Color32(70, 240, 240, 255), SettingsScope.User); // Cyan
|
||||
static UserSetting<Color> s_Color6 = new UserSetting<Color>(PathSettings.instance, "SplineData.color6", new Color32(250, 190, 190, 255), SettingsScope.User); // Pink
|
||||
static UserSetting<Color> s_Color7 = new UserSetting<Color>(PathSettings.instance, "SplineData.color7", new Color32(0, 128, 128, 255), SettingsScope.User); // Teal
|
||||
static UserSetting<Color>[] s_DefaultSplineColors = null;
|
||||
|
||||
static UserSetting<Color>[] defaultSplineColors
|
||||
{
|
||||
get
|
||||
{
|
||||
if(s_DefaultSplineColors == null)
|
||||
s_DefaultSplineColors = new UserSetting<Color>[]
|
||||
{
|
||||
s_Color0,
|
||||
s_Color1,
|
||||
s_Color2,
|
||||
s_Color3,
|
||||
s_Color4,
|
||||
s_Color5,
|
||||
s_Color6,
|
||||
s_Color7
|
||||
};
|
||||
|
||||
return s_DefaultSplineColors;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<Type, ISplineDataHandle> s_DrawerTypes = null;
|
||||
|
||||
static Dictionary<Type, ISplineDataHandle> drawerTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
if(s_DrawerTypes == null)
|
||||
InitializeDrawerTypes();
|
||||
return s_DrawerTypes;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool hasSplineDataIsSelection => s_SelectedSplineDataContainers.Count > 0;
|
||||
|
||||
static List<SplineDataContainer> s_SelectedSplineDataContainers = new List<SplineDataContainer>();
|
||||
public static List<SplineDataContainer> selectedSplineDataContainers => s_SelectedSplineDataContainers;
|
||||
|
||||
public static Action onSplineDataSelectionChanged;
|
||||
|
||||
static List<Component> s_AllComponents = new List<Component>();
|
||||
static List<Component> s_TmpComponents = new List<Component>();
|
||||
|
||||
internal class SplineDataContainer
|
||||
{
|
||||
public GameObject owner;
|
||||
public List<SplineDataElement> splineDataElements = new List<SplineDataElement>();
|
||||
}
|
||||
|
||||
internal class SplineDataElement
|
||||
{
|
||||
public FieldInfo splineDataField;
|
||||
public MethodInfo drawMethodInfo;
|
||||
public MethodInfo drawKeyframeMethodInfo;
|
||||
public ISplineDataHandle customDrawerInstance;
|
||||
public SplineDataHandleAttribute customDrawerAttribute;
|
||||
public MethodInfo initCustomDrawMethodInfo;
|
||||
public MethodInfo customSplineDataDrawHandleMethodInfo;
|
||||
public MethodInfo customDataPointDrawHandleMethodInfo;
|
||||
public SplineContainer splineContainer = null;
|
||||
public Component component = null;
|
||||
public bool displayed = true;
|
||||
public UserSetting<Color> color;
|
||||
public SplineDataHandlesDrawer.LabelType labelType = SplineDataHandlesDrawer.LabelType.None;
|
||||
}
|
||||
|
||||
static SplineDataHandlesManager()
|
||||
{
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
Selection.selectionChanged += OnSelectionChanged;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
static void OnPlayModeStateChanged(PlayModeStateChange playModeStateChange)
|
||||
{
|
||||
OnSelectionChanged();
|
||||
}
|
||||
|
||||
static void OnSelectionChanged()
|
||||
{
|
||||
UpdateSplineDataElements();
|
||||
}
|
||||
|
||||
static void InitializeDrawerTypes()
|
||||
{
|
||||
s_DrawerTypes = new Dictionary<Type, ISplineDataHandle>();
|
||||
var drawerTypes = TypeCache.GetTypesDerivedFrom(typeof(SplineDataHandle<>));
|
||||
|
||||
foreach(var drawerType in drawerTypes)
|
||||
{
|
||||
var attributes = drawerType.GetCustomAttributes(typeof(CustomSplineDataHandle));
|
||||
if(attributes.Any())
|
||||
{
|
||||
var drawerAttribute = (attributes.First() as CustomSplineDataHandle).m_Type;
|
||||
var drawerInstance = drawerType.Assembly.CreateInstance(drawerType.ToString()) as ISplineDataHandle;
|
||||
s_DrawerTypes.Add(drawerAttribute, drawerInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool VerifyComponentsIntegrity()
|
||||
{
|
||||
var selection = Selection.gameObjects;
|
||||
int componentCount = 0;
|
||||
|
||||
foreach(GameObject go in selection)
|
||||
{
|
||||
go.GetComponents(typeof(Component), s_TmpComponents);
|
||||
foreach(var component in s_TmpComponents)
|
||||
{
|
||||
if(s_AllComponents.Contains(component))
|
||||
componentCount++;
|
||||
else //A new component has been added
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Check if no components has been removed
|
||||
return componentCount == s_AllComponents.Count;
|
||||
}
|
||||
|
||||
static void UpdateSplineDataElements()
|
||||
{
|
||||
|
||||
if(Selection.activeGameObject == null)
|
||||
{
|
||||
if(s_SelectedSplineDataContainers.Count > 0)
|
||||
{
|
||||
s_SelectedSplineDataContainers.Clear();
|
||||
onSplineDataSelectionChanged?.Invoke();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
s_SelectedSplineDataContainers.Clear();
|
||||
|
||||
var gameObjects = Selection.gameObjects;
|
||||
|
||||
SplineDataContainer container = null;
|
||||
FieldInfo[] fieldInfos;
|
||||
|
||||
s_AllComponents.Clear();
|
||||
|
||||
int colorIndex = 0;
|
||||
foreach(GameObject go in gameObjects)
|
||||
{
|
||||
container = null;
|
||||
go.GetComponents(typeof(Component), s_TmpComponents);
|
||||
s_AllComponents.AddRange(s_TmpComponents);
|
||||
|
||||
foreach(Component component in s_TmpComponents)
|
||||
{
|
||||
if(component == null)
|
||||
continue;
|
||||
|
||||
fieldInfos = component.GetType().GetFields(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
||||
for(int i = 0; i < fieldInfos.Length; i++)
|
||||
{
|
||||
var fieldType = fieldInfos[i].FieldType;
|
||||
if(fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(SplineData<>))
|
||||
{
|
||||
SplineContainer splineContainer = go.GetComponent<SplineContainer>();
|
||||
|
||||
//If no spline container on the GameObject, check if the current Component is referencing one
|
||||
if(splineContainer == null)
|
||||
{
|
||||
var fi = fieldInfos.First(field => field.FieldType == typeof(SplineContainer));
|
||||
if(fi.FieldType == typeof(SplineContainer))
|
||||
splineContainer = (SplineContainer)fi.GetValue(component);
|
||||
}
|
||||
|
||||
//Skip this SplineData if no splineContainer can be automatically attributed
|
||||
if(splineContainer == null)
|
||||
continue;
|
||||
|
||||
Type splineDataType = fieldInfos[i].FieldType.GetTypeInfo().GenericTypeArguments[0];
|
||||
Type drawer = typeof(SplineDataHandlesDrawer);
|
||||
|
||||
SplineDataHandleAttribute splineDataAttribute = fieldInfos[i].GetCustomAttribute<SplineDataHandleAttribute>();
|
||||
ISplineDataHandle customDrawerInstance = null;
|
||||
MethodInfo initCustomDrawMethodInfo = null;
|
||||
MethodInfo customSplineDataHandleMethodInfo = null;
|
||||
MethodInfo customKeyframeHandleMethodInfo = null;
|
||||
|
||||
if(splineDataAttribute != null)
|
||||
{
|
||||
if(drawerTypes.ContainsKey(splineDataAttribute.GetType()))
|
||||
{
|
||||
var splineDataHandleInstance = drawerTypes[splineDataAttribute.GetType()];
|
||||
var splineDataHandleType = splineDataHandleInstance.GetType();
|
||||
var drawerDataType = splineDataHandleType.BaseType?.GenericTypeArguments[0];
|
||||
if(drawerDataType != splineDataType)
|
||||
{
|
||||
Debug.LogError($"The data type '{splineDataType}' used for field {fieldInfos[i].Name} in {component.GetType().Name} " +
|
||||
$"does not correspond to the type '{drawerDataType}' used in {splineDataHandleType.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
initCustomDrawMethodInfo =
|
||||
drawer.GetMethod("InitCustomHandles",
|
||||
BindingFlags.Static | BindingFlags.NonPublic
|
||||
)?.MakeGenericMethod(splineDataType);
|
||||
|
||||
var customDrawerMethodInfos = splineDataHandleType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
|
||||
customSplineDataHandleMethodInfo = GetCustomSplineDataDrawerMethodInfo(customDrawerMethodInfos, splineDataType);
|
||||
customKeyframeHandleMethodInfo = GetCustomKeyframeDrawerMethodInfo(customDrawerMethodInfos, splineDataType);
|
||||
|
||||
customDrawerInstance = splineDataHandleInstance;
|
||||
}
|
||||
}
|
||||
else
|
||||
Debug.LogError($"No valid SplineDataHandle<> type was found for drawerID = \"{splineDataAttribute.GetType()}\" (used in {component.gameObject.name}/{component.GetType().Name})");
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
MethodInfo drawerMethod = drawer.GetMethod("DrawSplineDataHandles", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
MethodInfo drawerMethodInfo = drawerMethod.MakeGenericMethod(splineDataType);
|
||||
MethodInfo dpDrawerMethod = drawer.GetMethod("DrawCustomHandles", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
MethodInfo dataPointDrawerMethodInfo = dpDrawerMethod.MakeGenericMethod(splineDataType);
|
||||
|
||||
var splineDataElement = new SplineDataElement()
|
||||
{
|
||||
splineDataField = fieldInfos[i],
|
||||
drawMethodInfo = drawerMethodInfo,
|
||||
drawKeyframeMethodInfo = dataPointDrawerMethodInfo,
|
||||
splineContainer = splineContainer,
|
||||
component = component,
|
||||
customDrawerInstance = customDrawerInstance,
|
||||
customDrawerAttribute = splineDataAttribute,
|
||||
initCustomDrawMethodInfo = initCustomDrawMethodInfo,
|
||||
customSplineDataDrawHandleMethodInfo = customSplineDataHandleMethodInfo,
|
||||
customDataPointDrawHandleMethodInfo = customKeyframeHandleMethodInfo,
|
||||
color = defaultSplineColors[colorIndex++ % defaultSplineColors.Length]
|
||||
};
|
||||
|
||||
if(container == null)
|
||||
{
|
||||
container = new SplineDataContainer() { owner = go };
|
||||
s_SelectedSplineDataContainers.Add(container);
|
||||
}
|
||||
container.splineDataElements.Add(splineDataElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onSplineDataSelectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
static MethodInfo GetCustomSplineDataDrawerMethodInfo(MethodInfo[] methodInfos, System.Type splineDataType)
|
||||
{
|
||||
foreach(var methodInfo in methodInfos)
|
||||
{
|
||||
//Checking if the method has the correct name
|
||||
if(!methodInfo.Name.Equals("DrawSplineData"))
|
||||
continue;
|
||||
|
||||
//Checking if the method as parameters
|
||||
var parameters = methodInfo.GetParameters();
|
||||
if(parameters.Length > 0)
|
||||
{
|
||||
//Is the first parameter with a generic type
|
||||
var parameterTypeInfo = parameters[0].ParameterType.GetTypeInfo();
|
||||
if(parameterTypeInfo.IsGenericType && parameterTypeInfo.GetGenericTypeDefinition() == typeof(SplineData<>))
|
||||
{
|
||||
var splineDataTargetType = parameterTypeInfo.GenericTypeArguments[0];
|
||||
if(splineDataTargetType == splineDataType)
|
||||
return methodInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static MethodInfo GetCustomKeyframeDrawerMethodInfo(MethodInfo[] methodInfos, System.Type splineDataType)
|
||||
{
|
||||
foreach(var methodInfo in methodInfos)
|
||||
{
|
||||
//Checking if the method has the correct name
|
||||
if(!methodInfo.Name.Equals("DrawDataPoint"))
|
||||
continue;
|
||||
|
||||
//Checking if the method as parameters
|
||||
var parameters = methodInfo.GetParameters();
|
||||
if(parameters.Length == 6)
|
||||
{
|
||||
//Is the first parameter with a generic type
|
||||
var parameterTypeInfo = parameters[4].ParameterType.GetTypeInfo();
|
||||
if(parameterTypeInfo.IsGenericType && parameterTypeInfo.GetGenericTypeDefinition() == typeof(SplineData<>))
|
||||
{
|
||||
var splineDataTargetType = parameterTypeInfo.GenericTypeArguments[0];
|
||||
if(splineDataTargetType == splineDataType)
|
||||
return methodInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void OnSceneGUI(SceneView view)
|
||||
{
|
||||
if(!VerifyComponentsIntegrity())
|
||||
OnSelectionChanged();
|
||||
|
||||
foreach(var sdContainer in s_SelectedSplineDataContainers)
|
||||
{
|
||||
foreach(var splineDataElement in sdContainer.splineDataElements)
|
||||
{
|
||||
if(!splineDataElement.displayed || splineDataElement.splineContainer == null)
|
||||
continue;
|
||||
|
||||
var mono = splineDataElement.component as MonoBehaviour;
|
||||
if((mono != null && !mono.isActiveAndEnabled) || !splineDataElement.splineContainer.isActiveAndEnabled)
|
||||
continue;
|
||||
|
||||
splineDataElement.drawMethodInfo?.Invoke(null,
|
||||
new object[]
|
||||
{
|
||||
splineDataElement.splineDataField.GetValue(splineDataElement.component),
|
||||
splineDataElement.component,
|
||||
splineDataElement.splineContainer.Spline,
|
||||
splineDataElement.splineContainer.transform.localToWorldMatrix,
|
||||
splineDataElement.color.value,
|
||||
splineDataElement.labelType
|
||||
});
|
||||
|
||||
if(splineDataElement.customDrawerInstance != null)
|
||||
{
|
||||
splineDataElement.customDrawerInstance.SetAttribute(splineDataElement.customDrawerAttribute);
|
||||
|
||||
var splineData = splineDataElement.splineDataField.GetValue(splineDataElement.component);
|
||||
var spline = splineDataElement.splineContainer.Spline;
|
||||
var localToWorld = splineDataElement.splineContainer.transform.localToWorldMatrix;
|
||||
|
||||
splineDataElement.initCustomDrawMethodInfo?.Invoke(null,
|
||||
new object[]
|
||||
{
|
||||
splineData,
|
||||
splineDataElement.customDrawerInstance
|
||||
});
|
||||
|
||||
splineDataElement.drawKeyframeMethodInfo?.Invoke(null,
|
||||
new object[]
|
||||
{
|
||||
splineData,
|
||||
splineDataElement.component,
|
||||
spline,
|
||||
localToWorld,
|
||||
splineDataElement.color.value,
|
||||
splineDataElement.customDrawerInstance,
|
||||
splineDataElement.customSplineDataDrawHandleMethodInfo,
|
||||
splineDataElement.customDataPointDrawHandleMethodInfo
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Handles.matrix = Matrix4x4.identity;
|
||||
Handles.color = Color.white;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7432e9702633479db8cad31be8640525
|
||||
timeCreated: 1635361594
|
|
@ -0,0 +1,90 @@
|
|||
using UnityEditor.SettingsManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
/// <summary>
|
||||
/// SplineGizmoUtility provides methods for drawing in-scene representations of Splines.
|
||||
/// </summary>
|
||||
public static class SplineGizmoUtility
|
||||
{
|
||||
[UserSetting]
|
||||
internal static UserSetting<Color> s_GizmosLineColor = new UserSetting<Color>(PathSettings.instance, "Gizmos.SplineColor", Color.blue, SettingsScope.User);
|
||||
|
||||
[UserSettingBlock("Gizmos")]
|
||||
static void GizmosColorPreferences(string searchContext)
|
||||
{
|
||||
s_GizmosLineColor.value = SettingsGUILayout.SettingsColorField("Splines Color", s_GizmosLineColor, searchContext);
|
||||
}
|
||||
|
||||
static Vector3[] s_Points;
|
||||
static Vector3 s_CameraUp;
|
||||
static Color s_OutlineColor = new Color(0f, 0f, 0f, .5f);
|
||||
|
||||
/// <summary>
|
||||
/// Draw a line gizmo for a <see cref="ISplineProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="provider">An object implementing the ISplineProvider interface. Usually this will be a MonoBehaviour.</param>
|
||||
public static void DrawGizmos(ISplineProvider provider)
|
||||
{
|
||||
var splines = provider.Splines;
|
||||
|
||||
if (splines == null)
|
||||
return;
|
||||
|
||||
s_CameraUp = SceneView.lastActiveSceneView.camera.transform.up;
|
||||
var localToWorld = ((MonoBehaviour)provider).transform.localToWorldMatrix;
|
||||
foreach(var spline in splines)
|
||||
{
|
||||
if(spline == null || spline.Count < 2)
|
||||
continue;
|
||||
|
||||
Vector3[] positions;
|
||||
SplineCacheUtility.GetCachedPositions(spline, out positions);
|
||||
|
||||
var color = Gizmos.color;
|
||||
var from = localToWorld.MultiplyPoint(positions[0]);
|
||||
var previousDir = Vector3.zero;
|
||||
for(int i = 1; i < positions.Length; ++i)
|
||||
{
|
||||
var to = localToWorld.MultiplyPoint(positions[i]);
|
||||
|
||||
var center = ( from + to ) / 2f;
|
||||
var size = .1f * HandleUtility.GetHandleSize(center);
|
||||
|
||||
var dir = to - from;
|
||||
var delta = previousDir.magnitude == 0 ? 1f :Vector3.Dot(previousDir, dir.normalized);
|
||||
//If the angle is too wide between 2 positions, take the previous position to draw the line
|
||||
if(delta < 0.9f)
|
||||
{
|
||||
Gizmos.color = color;
|
||||
DrawLineSegment(from, from + previousDir, size);
|
||||
from = from + previousDir;
|
||||
dir = to - from;
|
||||
}
|
||||
//Is the second position far enough to draw the segment
|
||||
if(i == positions.Length-1 || dir.magnitude > size)
|
||||
{
|
||||
Gizmos.color = color;
|
||||
DrawLineSegment(from, to, size);
|
||||
from = to;
|
||||
previousDir = Vector3.zero;
|
||||
}
|
||||
else
|
||||
previousDir = dir;
|
||||
}
|
||||
Gizmos.matrix = Matrix4x4.identity;
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawLineSegment(Vector3 from, Vector3 to, float size)
|
||||
{
|
||||
Gizmos.DrawLine(from, to);
|
||||
Gizmos.color = s_OutlineColor;
|
||||
//make the gizmo a little thicker
|
||||
var offset = size * s_CameraUp / 7.5f;
|
||||
Gizmos.DrawLine(from - offset, to - offset);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 004bce94384eadb4ebd753c96f4ecc5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,232 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
struct ColorScope : IDisposable
|
||||
{
|
||||
readonly Color m_PrevColor;
|
||||
|
||||
public ColorScope(Color color)
|
||||
{
|
||||
m_PrevColor = Handles.color;
|
||||
Handles.color = color;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Handles.color = m_PrevColor;
|
||||
}
|
||||
}
|
||||
|
||||
struct ZTestScope : IDisposable
|
||||
{
|
||||
readonly CompareFunction m_Original;
|
||||
|
||||
public ZTestScope(CompareFunction function)
|
||||
{
|
||||
m_Original = Handles.zTest;
|
||||
Handles.zTest = function;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Handles.zTest = m_Original;
|
||||
}
|
||||
}
|
||||
|
||||
static class SplineHandleUtility
|
||||
{
|
||||
const int k_MaxDecimals = 15;
|
||||
const int k_SegmentsPointCount = 30;
|
||||
static readonly Vector3[] s_ClosestPointArray = new Vector3[k_SegmentsPointCount];
|
||||
const float k_KnotPickingDistance = 18f;
|
||||
|
||||
static readonly Vector3[] s_LineBuffer = new Vector3[2];
|
||||
|
||||
internal static Ray TransformRay(Ray ray, Matrix4x4 matrix)
|
||||
{
|
||||
return new Ray(matrix.MultiplyPoint3x4(ray.origin), matrix.MultiplyVector(ray.direction));
|
||||
}
|
||||
|
||||
internal static Vector3 DoIncrementSnap(Vector3 position, Vector3 previousPosition)
|
||||
{
|
||||
var delta = position - previousPosition;
|
||||
|
||||
var right = Tools.handleRotation * Vector3.right;
|
||||
var up = Tools.handleRotation * Vector3.up;
|
||||
var forward = Tools.handleRotation * Vector3.forward;
|
||||
|
||||
var snappedDelta =
|
||||
Snapping.Snap(Vector3.Dot(delta, right), EditorSnapSettings.move[0]) * right +
|
||||
Snapping.Snap(Vector3.Dot(delta, up), EditorSnapSettings.move[1]) * up +
|
||||
Snapping.Snap(Vector3.Dot(delta, forward), EditorSnapSettings.move[2]) * forward;
|
||||
return previousPosition + snappedDelta;
|
||||
}
|
||||
|
||||
static Vector3 SnapToGrid(Vector3 position)
|
||||
{
|
||||
//todo Temporary version, waiting for a trunk PR to land to move to the commented version:
|
||||
//#if UNITY_2022_2_OR_NEWER
|
||||
// if(EditorSnapSettings.gridSnapActive)
|
||||
// return Snapping.Snap(position, EditorSnapSettings.gridSize, SnapAxis.All);
|
||||
//#else
|
||||
GameObject tmp = new GameObject();
|
||||
tmp.hideFlags = HideFlags.HideAndDontSave;
|
||||
var trs = tmp.transform;
|
||||
trs.position = position;
|
||||
Handles.SnapToGrid(new []{trs});
|
||||
var snapped = trs.position;
|
||||
Object.DestroyImmediate(tmp);
|
||||
|
||||
return snapped;
|
||||
//#endif
|
||||
}
|
||||
|
||||
internal static bool GetPointOnSurfaces(Vector2 mousePosition, out Vector3 point, out Vector3 normal)
|
||||
{
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
if(HandleUtility.PlaceObject(mousePosition, out point, out normal))
|
||||
{
|
||||
if(EditorSnapSettings.gridSnapEnabled)
|
||||
point = SnapToGrid(point);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
var ray = HandleUtility.GUIPointToWorldRay(mousePosition);
|
||||
|
||||
#if !UNITY_2020_1_OR_NEWER
|
||||
if (Physics.Raycast(ray, out RaycastHit hit))
|
||||
{
|
||||
point = hit.point;
|
||||
normal = hit.normal;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
//Backup if couldn't find a surface
|
||||
var constraint = new Plane(Vector3.up, Vector3.zero); //This should be in the direction of the current grid
|
||||
if (constraint.Raycast(ray, out float distance))
|
||||
{
|
||||
normal = constraint.normal;
|
||||
point = ray.origin + ray.direction * distance;
|
||||
|
||||
if(EditorSnapSettings.gridSnapEnabled)
|
||||
point = SnapToGrid(point);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
point = normal = Vector3.zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void DrawLineWithWidth(Vector3 a, Vector3 b, float width, Texture2D lineAATex = null)
|
||||
{
|
||||
s_LineBuffer[0] = a;
|
||||
s_LineBuffer[1] = b;
|
||||
|
||||
Handles.DrawAAPolyLine(lineAATex, width, s_LineBuffer);
|
||||
}
|
||||
|
||||
public static float DistanceToKnot(Vector3 position)
|
||||
{
|
||||
return DistanceToCircle(position, k_KnotPickingDistance);
|
||||
}
|
||||
|
||||
public static float DistanceToCircle(Vector3 point, float radius)
|
||||
{
|
||||
Vector3 screenPos = HandleUtility.WorldToGUIPointWithDepth(point);
|
||||
if (screenPos.z < 0)
|
||||
return float.MaxValue;
|
||||
|
||||
return Mathf.Max(0, Vector2.Distance(screenPos, Event.current.mousePosition) - radius);
|
||||
}
|
||||
|
||||
internal static Vector3 RoundBasedOnMinimumDifference(Vector3 position)
|
||||
{
|
||||
var minDiff = GetMinDifference(position);
|
||||
position.x = RoundBasedOnMinimumDifference(position.x, minDiff.x);
|
||||
position.y = RoundBasedOnMinimumDifference(position.y, minDiff.y);
|
||||
position.z = RoundBasedOnMinimumDifference(position.z, minDiff.z);
|
||||
return position;
|
||||
}
|
||||
|
||||
internal static Vector3 GetMinDifference(Vector3 position)
|
||||
{
|
||||
return Vector3.one * (HandleUtility.GetHandleSize(position) / 80f);
|
||||
}
|
||||
|
||||
internal static float RoundBasedOnMinimumDifference(float valueToRound, float minDifference)
|
||||
{
|
||||
var numberOfDecimals = Mathf.Clamp(-Mathf.FloorToInt(Mathf.Log10(Mathf.Abs(minDifference))), 0, k_MaxDecimals);
|
||||
return (float)Math.Round(valueToRound, numberOfDecimals, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
public static void GetNearestPointOnCurve(CurveData curve, out Vector3 position, out float t)
|
||||
{
|
||||
Vector3 closestA = Vector3.zero;
|
||||
Vector3 closestB = Vector3.zero;
|
||||
float closestDist = float.MaxValue;
|
||||
int closestSegmentFirstPoint = -1;
|
||||
|
||||
GetCurveSegments(curve, s_ClosestPointArray);
|
||||
for (int j = 0; j < s_ClosestPointArray.Length - 1; ++j)
|
||||
{
|
||||
Vector3 a = s_ClosestPointArray[j];
|
||||
Vector3 b = s_ClosestPointArray[j + 1];
|
||||
float dist = HandleUtility.DistanceToLine(a, b);
|
||||
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestA = a;
|
||||
closestB = b;
|
||||
closestDist = dist;
|
||||
closestSegmentFirstPoint = j;
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate position
|
||||
Vector2 screenPosA = HandleUtility.WorldToGUIPoint(closestA);
|
||||
Vector2 screenPosB = HandleUtility.WorldToGUIPoint(closestB);
|
||||
Vector2 relativePoint = Event.current.mousePosition - screenPosA;
|
||||
Vector2 lineDirection = screenPosB - screenPosA;
|
||||
float length = lineDirection.magnitude;
|
||||
float dot = Vector3.Dot(lineDirection, relativePoint);
|
||||
if (length > .000001f)
|
||||
dot /= length * length;
|
||||
dot = Mathf.Clamp01(dot);
|
||||
position = Vector3.Lerp(closestA, closestB, dot);
|
||||
|
||||
//Calculate percent on curve's segment
|
||||
float percentPerSegment = 1.0f / (k_SegmentsPointCount - 1);
|
||||
float percentA = closestSegmentFirstPoint * percentPerSegment;
|
||||
float lengthAB = (closestB - closestA).magnitude;
|
||||
float lengthAToClosest = (position - closestA).magnitude;
|
||||
t = percentA + percentPerSegment * (lengthAToClosest / lengthAB);
|
||||
}
|
||||
|
||||
internal static void GetCurveSegments(CurveData curve, Vector3[] results)
|
||||
{
|
||||
if (!curve.IsValid())
|
||||
throw new ArgumentException(nameof(curve));
|
||||
|
||||
if (results == null)
|
||||
throw new ArgumentNullException(nameof(results));
|
||||
|
||||
if (results.Length < 2)
|
||||
throw new ArgumentException("Get curve segments requires a results array of at least two points", nameof(results));
|
||||
|
||||
var segmentCount = results.Length - 1;
|
||||
float segmentPercentage = 1f / segmentCount;
|
||||
var path = curve.a.spline;
|
||||
for (int i = 0; i <= segmentCount; ++i)
|
||||
{
|
||||
results[i] = path.GetPointOnCurve(curve, i * segmentPercentage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
fileFormatVersion: 2
|
||||
<<<<<<< HEAD:Editor/InternalBridge/AssemblyInfo.cs.meta
|
||||
guid: 2da6d0ba602194831a1a64e5bc72f6ad
|
||||
=======
|
||||
guid: d678438588162a94e8d14b40288240af
|
||||
>>>>>>> master:Editor/Utilities/SplineHandleUtility.cs.meta
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,31 @@
|
|||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
static class SplineSelectionUtility
|
||||
{
|
||||
internal static bool IsSelectable(IEditableSpline spline, int knotIndex, ISplineElement element)
|
||||
{
|
||||
if (element is EditableTangent)
|
||||
{
|
||||
var ownerKnot = spline.GetKnot(knotIndex);
|
||||
if (ownerKnot is BezierEditableKnot knot)
|
||||
{
|
||||
// For open splines, tangentIn of first knot and tangentOut of last knot should not be selectable.
|
||||
if (!spline.closed)
|
||||
{
|
||||
if ((knotIndex == 0 && knot.tangentIn == element) ||
|
||||
(knotIndex == spline.knotCount - 1 && knot.tangentOut == element))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tangents should not be selectable if knot is Linear.
|
||||
if (knot.mode == BezierEditableKnot.Mode.Linear)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9480991a8f9cd457891d0c23ec511b49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue