initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
[Serializable]
|
||||
struct CurveData
|
||||
{
|
||||
public static readonly CurveData empty = new CurveData
|
||||
{
|
||||
a = null,
|
||||
b = null
|
||||
};
|
||||
|
||||
public EditableKnot a { get; private set; }
|
||||
public EditableKnot b { get; private set; }
|
||||
|
||||
public CurveData(EditableKnot firstKnot)
|
||||
{
|
||||
a = firstKnot;
|
||||
|
||||
//If first knot is last knot of the spline, use index 0 for the closing curve
|
||||
var path = firstKnot.spline;
|
||||
int nextIndex = firstKnot.index + 1;
|
||||
if (nextIndex >= path.knotCount)
|
||||
nextIndex = 0;
|
||||
|
||||
b = path.GetKnot(nextIndex);
|
||||
}
|
||||
|
||||
public CurveData(EditableKnot firstKnot, EditableKnot lastKnot)
|
||||
{
|
||||
a = firstKnot;
|
||||
b = lastKnot;
|
||||
}
|
||||
|
||||
public CurveData(IEditableSpline spline, int firstIndex) : this(spline.GetKnot(firstIndex)){}
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return a != null && b != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 92217b25452f8a94998d750fee5381d1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
[Serializable]
|
||||
class EditableKnot : ISplineElement
|
||||
{
|
||||
internal static event Action<EditableKnot> knotModified;
|
||||
|
||||
[SerializeField]
|
||||
float3 m_LocalPosition;
|
||||
|
||||
[SerializeField]
|
||||
quaternion m_LocalRotation = quaternion.identity;
|
||||
|
||||
public IEditableSpline spline { get; internal set; }
|
||||
public int index { get; internal set; }
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return index >= 0;
|
||||
}
|
||||
|
||||
/// <summary> Matrix that transforms a point from local (knot) into world space. </summary>
|
||||
public Matrix4x4 localToWorldMatrix => spline.localToWorldMatrix * Matrix4x4.TRS(localPosition, localRotation, Vector3.one);
|
||||
/// <summary> Matrix that transforms a point from world space into local (knot) space. </summary>
|
||||
public Matrix4x4 worldToLocalMatrix => localToWorldMatrix.inverse;
|
||||
|
||||
public EditableKnot GetPrevious()
|
||||
{
|
||||
return spline.GetPreviousKnot(index, out EditableKnot previous) ? previous : null;
|
||||
}
|
||||
|
||||
public EditableKnot GetNext()
|
||||
{
|
||||
return spline.GetNextKnot(index, out EditableKnot next) ? next : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// World space position of the knot.
|
||||
/// </summary>
|
||||
public float3 position
|
||||
{
|
||||
get => spline.localToWorldMatrix.MultiplyPoint3x4(localPosition);
|
||||
set => localPosition = spline.worldToLocalMatrix.MultiplyPoint3x4(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Local (spline space) position of the knot.
|
||||
/// </summary>
|
||||
public float3 localPosition
|
||||
{
|
||||
get => m_LocalPosition;
|
||||
set
|
||||
{
|
||||
if (m_LocalPosition.Equals(value))
|
||||
return;
|
||||
|
||||
m_LocalPosition = value;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// World space rotation of the knot.
|
||||
/// </summary>
|
||||
public quaternion rotation
|
||||
{
|
||||
get => spline.localToWorldMatrix.rotation * localRotation;
|
||||
set => localRotation = math.mul(spline.worldToLocalMatrix.rotation, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Local (spline space) rotation of the knot.
|
||||
/// </summary>
|
||||
public quaternion localRotation
|
||||
{
|
||||
get => m_LocalRotation;
|
||||
set
|
||||
{
|
||||
if (m_LocalRotation.Equals(value))
|
||||
return;
|
||||
|
||||
m_LocalRotation = math.normalize(value);
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How many editable tangents a knot contains. Cubic bezier splines contain 2 tangents, except at the ends of
|
||||
/// a Spline that is not closed, in which case the knot contains a single tangent. Other spline type representations
|
||||
/// may contain more or fewer tangents (ex, a Catmull-Rom spline does not expose any editable tangents).
|
||||
/// </summary>
|
||||
public int tangentCount => spline.tangentsPerKnot;
|
||||
|
||||
public virtual void Copy(EditableKnot other)
|
||||
{
|
||||
spline = other.spline;
|
||||
index = other.index;
|
||||
m_LocalPosition = other.localPosition;
|
||||
m_LocalRotation = other.localRotation;
|
||||
for (int i = 0, count = math.min(tangentCount, other.tangentCount); i < count; ++i)
|
||||
GetTangent(i).Copy(other.GetTangent(i));
|
||||
}
|
||||
|
||||
public void SetDirty()
|
||||
{
|
||||
knotModified?.Invoke(this);
|
||||
spline?.SetDirty();
|
||||
}
|
||||
|
||||
internal virtual EditableTangent GetTangent(int index) { return null; }
|
||||
|
||||
public virtual void ValidateData() {}
|
||||
public virtual void OnPathUpdatedFromTarget() {}
|
||||
public virtual void OnKnotInsertedOnCurve(EditableKnot previous, EditableKnot next, float t) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c9eab45bfb720d641b442af1b8ad2543
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using UObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
interface IEditableSpline
|
||||
{
|
||||
bool canBeClosed { get; set; }
|
||||
bool closed { get; set; }
|
||||
int knotCount { get; }
|
||||
/// <summary>
|
||||
/// How many editable tangents a knot contains. Cubic bezier splines contain 2 tangents, except at the ends of
|
||||
/// a Spline that is not closed, in which case the knot contains a single tangent. Other spline type representations
|
||||
/// may contain more or fewer tangents (ex, a Catmull-Rom spline does not expose any editable tangents).
|
||||
/// </summary>
|
||||
int tangentsPerKnot { get; }
|
||||
/// <summary> Matrix that transforms a point from local (spline) into world space. </summary>
|
||||
Matrix4x4 localToWorldMatrix { get; }
|
||||
/// <summary> Matrix that transforms a point from world space into local (spline) space. </summary>
|
||||
Matrix4x4 worldToLocalMatrix { get; }
|
||||
|
||||
EditableKnot GetKnot(int index);
|
||||
bool GetPreviousKnot(int index, out EditableKnot knot);
|
||||
bool GetNextKnot(int index, out EditableKnot knot);
|
||||
void Resize(int targetKnotCount);
|
||||
EditableKnot AddKnot();
|
||||
void RemoveKnotAt(int index);
|
||||
EditableKnot InsertKnot(int index);
|
||||
CurveData GetPreviewCurveForEndKnot(float3 point, float3 normal, float3 tangentOut);
|
||||
void OnKnotAddedAtEnd(EditableKnot knot, float3 normal, float3 tangentOut);
|
||||
float3 GetPointOnCurve(CurveData curve, float t);
|
||||
/// <summary>
|
||||
/// Given an editable knot, returns its in and out tangents in local (spline) space.
|
||||
/// </summary>
|
||||
/// <param name="knot">An editable knot.</param>
|
||||
/// <param name="localTangentIn">Knot's in tangent in local (spline) space.</param>
|
||||
/// <param name="localTangentOut">Knot's out tangent in local (spline) space.</param>
|
||||
void GetLocalTangents(EditableKnot knot, out float3 localTangentIn, out float3 localTangentOut);
|
||||
void SetDirty();
|
||||
void ToBezier(List<BezierKnot> results);
|
||||
void FromBezier(IReadOnlyList<BezierKnot> knots);
|
||||
|
||||
bool isDirty { get; set; }
|
||||
UObject conversionTarget { get; set; }
|
||||
int conversionIndex { get; set; }
|
||||
|
||||
void ValidateData();
|
||||
void CopyConversionDataFrom(IEditableSpline spline);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
abstract class EditableSpline<T> : IEditableSpline
|
||||
where T : EditableKnot, new()
|
||||
{
|
||||
const int k_MinimumKnotCountToBeClosed = 3;
|
||||
|
||||
//Serialized fields will be used for tools inspector
|
||||
[SerializeField]
|
||||
List<T> m_Knots = new List<T>();
|
||||
|
||||
[SerializeField]
|
||||
bool m_Closed = false;
|
||||
|
||||
UObject m_ConversionTarget;
|
||||
int m_ConversionIndex;
|
||||
|
||||
bool m_IsDirty = false;
|
||||
|
||||
bool m_CanBeClosed = true;
|
||||
|
||||
protected EditableKnot m_PreviewKnotA;
|
||||
protected EditableKnot m_PreviewKnotB;
|
||||
|
||||
public Matrix4x4 localToWorldMatrix =>
|
||||
m_ConversionTarget != null && m_ConversionTarget is Component component
|
||||
? component.transform.localToWorldMatrix
|
||||
: Matrix4x4.identity;
|
||||
|
||||
public Matrix4x4 worldToLocalMatrix => localToWorldMatrix.inverse;
|
||||
|
||||
UObject IEditableSpline.conversionTarget
|
||||
{
|
||||
get => m_ConversionTarget;
|
||||
set => m_ConversionTarget = value;
|
||||
}
|
||||
|
||||
//the index in the target array created at conversion
|
||||
int IEditableSpline.conversionIndex
|
||||
{
|
||||
get => m_ConversionIndex;
|
||||
set => m_ConversionIndex = value;
|
||||
}
|
||||
|
||||
void IEditableSpline.CopyConversionDataFrom(IEditableSpline spline)
|
||||
{
|
||||
m_ConversionTarget = spline.conversionTarget;
|
||||
m_ConversionIndex = spline.conversionIndex;
|
||||
}
|
||||
|
||||
void IEditableSpline.ValidateData()
|
||||
{
|
||||
UpdateKnotIndices();
|
||||
foreach (var knot in m_Knots)
|
||||
{
|
||||
knot.spline = this;
|
||||
knot.ValidateData();
|
||||
}
|
||||
}
|
||||
|
||||
public bool canBeClosed
|
||||
{
|
||||
get => m_CanBeClosed;
|
||||
set
|
||||
{
|
||||
m_CanBeClosed = value;
|
||||
if (!m_CanBeClosed)
|
||||
{
|
||||
m_Closed = false;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool closed
|
||||
{
|
||||
get => knotCount >= k_MinimumKnotCountToBeClosed && m_Closed;
|
||||
set
|
||||
{
|
||||
if (m_Closed == value || !m_CanBeClosed)
|
||||
return;
|
||||
|
||||
m_Closed = value;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
bool IEditableSpline.isDirty
|
||||
{
|
||||
get => m_IsDirty;
|
||||
set => m_IsDirty = value;
|
||||
}
|
||||
|
||||
public int knotCount => m_Knots.Count;
|
||||
|
||||
public virtual int tangentsPerKnot => 0;
|
||||
|
||||
EditableKnot IEditableSpline.GetKnot(int index)
|
||||
{
|
||||
return GetKnot(index);
|
||||
}
|
||||
|
||||
public T GetKnot(int index)
|
||||
{
|
||||
return m_Knots[index];
|
||||
}
|
||||
|
||||
public bool GetPreviousKnot(int index, out EditableKnot knot)
|
||||
{
|
||||
bool result = GetPreviousKnot(index, out T rawKnot);
|
||||
knot = rawKnot;
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool GetPreviousKnot(int index, out T knot)
|
||||
{
|
||||
if (knotCount > 0)
|
||||
{
|
||||
int next = index - 1;
|
||||
if (next >= 0)
|
||||
{
|
||||
knot = m_Knots[next];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (closed)
|
||||
{
|
||||
knot = m_Knots[m_Knots.Count - 1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
knot = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool GetNextKnot(int index, out EditableKnot knot)
|
||||
{
|
||||
if (knotCount > 0)
|
||||
{
|
||||
int next = index + 1;
|
||||
if (next < m_Knots.Count)
|
||||
{
|
||||
knot = m_Knots[next];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (closed)
|
||||
{
|
||||
knot = m_Knots[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
knot = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Resize(int targetKnotCount)
|
||||
{
|
||||
if (knotCount > targetKnotCount)
|
||||
{
|
||||
m_Knots.RemoveRange(targetKnotCount, knotCount - targetKnotCount);
|
||||
}
|
||||
else if (knotCount < targetKnotCount)
|
||||
{
|
||||
while (knotCount < targetKnotCount)
|
||||
{
|
||||
AddKnot();
|
||||
}
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
public EditableKnot AddKnot()
|
||||
{
|
||||
var knot = CreateKnot();
|
||||
knot.index = m_Knots.Count;
|
||||
m_Knots.Add(knot);
|
||||
SetDirty();
|
||||
return knot;
|
||||
}
|
||||
|
||||
public void RemoveKnotAt(int index)
|
||||
{
|
||||
EditableKnot knot = m_Knots[index];
|
||||
|
||||
SplineSelection.Remove(knot);
|
||||
SplineSelection.OnKnotRemoved(this, index);
|
||||
|
||||
knot.index = -1;
|
||||
m_Knots.RemoveAt(index);
|
||||
UpdateKnotIndices();
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
public EditableKnot InsertKnot(int index)
|
||||
{
|
||||
var knot = CreateKnot();
|
||||
m_Knots.Insert(index, knot);
|
||||
UpdateKnotIndices();
|
||||
SetDirty();
|
||||
|
||||
SplineSelection.MoveAllIndexUpFromIndexToEnd(this, index);
|
||||
return knot;
|
||||
}
|
||||
|
||||
protected void CreatePreviewKnotsIfNeeded()
|
||||
{
|
||||
if (m_PreviewKnotA == null)
|
||||
m_PreviewKnotA = CreateKnot();
|
||||
|
||||
if (m_PreviewKnotB == null)
|
||||
m_PreviewKnotB = CreateKnot();
|
||||
}
|
||||
|
||||
void UpdateKnotIndices()
|
||||
{
|
||||
for (int i = 0; i < m_Knots.Count; ++i)
|
||||
{
|
||||
m_Knots[i].index = i;
|
||||
}
|
||||
}
|
||||
|
||||
T CreateKnot()
|
||||
{
|
||||
return new T { spline = this };
|
||||
}
|
||||
|
||||
public void SetDirty()
|
||||
{
|
||||
m_IsDirty = true;
|
||||
}
|
||||
|
||||
public virtual void OnKnotAddedAtEnd(EditableKnot knot, float3 normal, float3 tangentOut) {}
|
||||
public abstract float3 GetPointOnCurve(CurveData curve, float t);
|
||||
public abstract void GetLocalTangents(EditableKnot knot, out float3 localTangentIn, out float3 localTangentOut);
|
||||
public abstract CurveData GetPreviewCurveForEndKnot(float3 point, float3 normal, float3 tangentOut);
|
||||
public abstract void ToBezier(List<BezierKnot> results);
|
||||
public abstract void FromBezier(IReadOnlyList<BezierKnot> knots);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7e98edb19c8e6294293225f04c7d4a67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.Splines;
|
||||
using UObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
static class EditableSplineManager
|
||||
{
|
||||
// ONLY FOR TESTS. Used to add a path to the manager without requiring all the loops to get to it.
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal sealed class TestManagedSpline : IDisposable
|
||||
{
|
||||
readonly UObject m_Target;
|
||||
|
||||
public TestManagedSpline(UObject target, IEditableSpline spline)
|
||||
{
|
||||
m_Target = target;
|
||||
spline.conversionTarget = m_Target;
|
||||
if (!s_Splines.TryGetValue(target, out TargetData data))
|
||||
{
|
||||
data = new TargetData();
|
||||
s_Splines.Add(target, data);
|
||||
}
|
||||
|
||||
data.RawSplines.Clear();
|
||||
data.EditableSplines = new[] { spline };
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Target != null)
|
||||
s_Splines.Remove(m_Target);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TargetData
|
||||
{
|
||||
public readonly List<Spline> RawSplines = new List<Spline>();
|
||||
public IEditableSpline[] EditableSplines = new IEditableSpline[0];
|
||||
}
|
||||
|
||||
static readonly List<UObject> s_OwnersBuffer = new List<UObject>();
|
||||
static readonly Dictionary<UObject, TargetData> s_Splines = new Dictionary<UObject, TargetData>();
|
||||
static readonly Dictionary<UObject, TargetData> s_SplinesBackup = new Dictionary<UObject, TargetData>();
|
||||
|
||||
static EditableSplineManager()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload += OnWillDomainReload;
|
||||
Selection.selectionChanged += OnSelectionChanged;
|
||||
Undo.undoRedoPerformed += OnUndoRedoPerformed;
|
||||
OnSelectionChanged();
|
||||
}
|
||||
|
||||
static void OnWillDomainReload()
|
||||
{
|
||||
FreeEntireCache();
|
||||
}
|
||||
|
||||
static void OnSelectionChanged()
|
||||
{
|
||||
UObject[] selection = Selection.GetFiltered(typeof(ISplineProvider), SelectionMode.Editable);
|
||||
UpdateSelection(selection);
|
||||
}
|
||||
|
||||
static void OnUndoRedoPerformed()
|
||||
{
|
||||
FreeEntireCache();
|
||||
}
|
||||
|
||||
internal static bool TryGetTargetData(UObject target, out TargetData targetData)
|
||||
{
|
||||
return s_Splines.TryGetValue(target, out targetData);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<IEditableSpline> GetEditableSplines(UObject target, bool createIfNotCached = true)
|
||||
{
|
||||
if (target == null)
|
||||
return null;
|
||||
|
||||
if (!s_Splines.TryGetValue(target, out TargetData result))
|
||||
{
|
||||
if (!createIfNotCached)
|
||||
return null;
|
||||
|
||||
var splineProvider = target as ISplineProvider;
|
||||
if (splineProvider == null)
|
||||
return null;
|
||||
|
||||
TargetData data = new TargetData();
|
||||
|
||||
var targetSplines = splineProvider.Splines;
|
||||
if (targetSplines != null)
|
||||
data.RawSplines.AddRange(targetSplines);
|
||||
|
||||
s_Splines.Add(target, data);
|
||||
result = data;
|
||||
|
||||
SplineConversionUtility.UpdateEditableSplinesForTarget(target);
|
||||
}
|
||||
|
||||
return result.EditableSplines;
|
||||
}
|
||||
|
||||
public static void GetTargetsOfType(Type type, List<UObject> results)
|
||||
{
|
||||
ValidatePathOwners();
|
||||
results.Clear();
|
||||
foreach (var target in s_Splines.Keys)
|
||||
{
|
||||
if (target != null && type.IsInstanceOfType(target))
|
||||
{
|
||||
results.Add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnsureCacheForTarget(UObject target)
|
||||
{
|
||||
GetEditableSplines(target);
|
||||
}
|
||||
|
||||
public static void UpdateSelection(IEnumerable<UObject> selected)
|
||||
{
|
||||
if (selected == null)
|
||||
return;
|
||||
|
||||
//Copy to backup
|
||||
s_SplinesBackup.Clear();
|
||||
foreach (var keyValuePair in s_Splines)
|
||||
{
|
||||
s_SplinesBackup.Add(keyValuePair.Key, keyValuePair.Value);
|
||||
}
|
||||
|
||||
//Copy all that are still selected to real dictionary and ensure cache for newly selected
|
||||
s_Splines.Clear();
|
||||
foreach (var target in selected)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
if (s_SplinesBackup.TryGetValue(target, out TargetData data))
|
||||
{
|
||||
s_Splines.Add(target, data);
|
||||
s_SplinesBackup.Remove(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureCacheForTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void FreeEntireCache()
|
||||
{
|
||||
s_Splines.Clear();
|
||||
}
|
||||
|
||||
public static void FreeCacheForTarget(UObject target)
|
||||
{
|
||||
if (s_Splines.TryGetValue(target, out TargetData data))
|
||||
{
|
||||
s_Splines.Remove(target);
|
||||
}
|
||||
}
|
||||
|
||||
static void ValidatePathOwners()
|
||||
{
|
||||
s_OwnersBuffer.Clear();
|
||||
foreach (var data in s_Splines)
|
||||
{
|
||||
// A dictionary key will never be fully null but the object could be destroyed
|
||||
if (data.Key == null)
|
||||
s_OwnersBuffer.Add(data.Key);
|
||||
}
|
||||
|
||||
foreach (var o in s_OwnersBuffer)
|
||||
{
|
||||
s_Splines.Remove(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 09cdc9b3ee4c5964082e9f6bae28aab6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
[Serializable]
|
||||
sealed class EditableTangent : ISplineElement
|
||||
{
|
||||
internal event Action directionChanged;
|
||||
|
||||
[SerializeField]
|
||||
float3 m_LocalPosition;
|
||||
|
||||
/// <summary> Local (knot space) position of the tangent. </summary>
|
||||
public float3 localPosition
|
||||
{
|
||||
get => m_LocalPosition;
|
||||
set
|
||||
{
|
||||
if (m_LocalPosition.Equals(value))
|
||||
return;
|
||||
|
||||
m_LocalPosition = value;
|
||||
directionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> World space direction of the tangent. </summary>
|
||||
public float3 direction
|
||||
{
|
||||
get => owner.localToWorldMatrix.MultiplyVector(localPosition);
|
||||
set => localPosition = owner.worldToLocalMatrix.MultiplyVector(value);
|
||||
}
|
||||
|
||||
/// <summary> World space position of the tangent. </summary>
|
||||
public float3 position
|
||||
{
|
||||
get => owner.localToWorldMatrix.MultiplyPoint3x4(localPosition);
|
||||
set => localPosition = owner.worldToLocalMatrix.MultiplyPoint3x4(value);
|
||||
}
|
||||
|
||||
internal void SetLocalPositionNoNotify(float3 localPosition)
|
||||
{
|
||||
m_LocalPosition = localPosition;
|
||||
}
|
||||
|
||||
public int tangentIndex { get; private set; }
|
||||
|
||||
public EditableKnot owner { get; private set; }
|
||||
|
||||
/// <summary> Matrix that transforms a point from local (tangent) into world space. </summary>
|
||||
public Matrix4x4 localToWorldMatrix => owner.localToWorldMatrix *
|
||||
Matrix4x4.TRS(localPosition, quaternion.identity, Vector3.one);
|
||||
/// <summary> Matrix that transforms a point from world space into local (tangent) space. </summary>
|
||||
public Matrix4x4 worldToLocalMatrix => localToWorldMatrix.inverse;
|
||||
|
||||
public EditableTangent() : this(null, -1) {}
|
||||
|
||||
public EditableTangent(EditableKnot owner, int tangentIndex)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.tangentIndex = tangentIndex;
|
||||
}
|
||||
|
||||
public void Copy(EditableTangent other)
|
||||
{
|
||||
tangentIndex = other.tangentIndex;
|
||||
m_LocalPosition = other.localPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b5cfd6cf4523fe044831ed4f356a5472
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using UnityEditor.SettingsManagement;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
sealed class PathSettings
|
||||
{
|
||||
static Settings s_SettingsInstance;
|
||||
|
||||
public static Settings instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_SettingsInstance == null)
|
||||
s_SettingsInstance = new Settings(new [] { new UserSettingsRepository() });
|
||||
return s_SettingsInstance;
|
||||
}
|
||||
}
|
||||
|
||||
// Register a new SettingsProvider that will scrape the owning assembly for [UserSetting] marked fields.
|
||||
[SettingsProvider]
|
||||
static SettingsProvider CreateSettingsProvider()
|
||||
{
|
||||
var provider = new UserSettingsProvider("Preferences/Splines",
|
||||
instance,
|
||||
new[] { typeof(PathSettings).Assembly });
|
||||
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0834e0204621424449fe7f88d7127f07
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
[Serializable]
|
||||
struct SelectableSplineElement : IEquatable<SelectableSplineElement>, IEquatable<EditableKnot>, IEquatable<EditableTangent>
|
||||
{
|
||||
public Object target;
|
||||
public int pathIndex;
|
||||
public int knotIndex;
|
||||
public int tangentIndex; //-1 if knot
|
||||
|
||||
public SelectableSplineElement(ISplineElement element)
|
||||
{
|
||||
target = default;
|
||||
pathIndex = -1;
|
||||
knotIndex = -1;
|
||||
tangentIndex = -1;
|
||||
|
||||
EditableKnot knot = null;
|
||||
if (element is EditableKnot knotElement)
|
||||
knot = knotElement;
|
||||
else if (element is EditableTangent tangent)
|
||||
{
|
||||
knot = tangent.owner;
|
||||
tangentIndex = tangent.tangentIndex;
|
||||
}
|
||||
|
||||
if (knot != null)
|
||||
{
|
||||
target = knot.spline.conversionTarget;
|
||||
pathIndex = knot.spline.conversionIndex;
|
||||
knotIndex = knot.index;
|
||||
}
|
||||
}
|
||||
|
||||
public bool isTangent => tangentIndex >= 0;
|
||||
public bool isKnot => tangentIndex < 0;
|
||||
|
||||
public bool Equals(EditableKnot other)
|
||||
{
|
||||
return IsTargetedKnot(other) && tangentIndex < 0;
|
||||
}
|
||||
|
||||
public bool Equals(EditableTangent other)
|
||||
{
|
||||
return other != null && IsTargetedKnot(other.owner) && tangentIndex == other.tangentIndex;
|
||||
}
|
||||
|
||||
public bool IsFromPath(IEditableSpline spline)
|
||||
{
|
||||
var pathInternal = spline;
|
||||
return pathInternal.conversionTarget == target && pathInternal.conversionIndex == pathIndex;
|
||||
}
|
||||
|
||||
bool IsTargetedKnot(EditableKnot knot)
|
||||
{
|
||||
if (knot == null)
|
||||
return false;
|
||||
|
||||
return knotIndex == knot.index
|
||||
&& pathIndex == knot.spline.conversionIndex
|
||||
&& target == knot.spline.conversionTarget;
|
||||
}
|
||||
|
||||
public bool Equals(SelectableSplineElement other)
|
||||
{
|
||||
return target == other.target && pathIndex == other.pathIndex && knotIndex == other.knotIndex && tangentIndex == other.tangentIndex;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
return obj is SelectableSplineElement other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = (target != null ? target.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ pathIndex;
|
||||
hashCode = (hashCode * 397) ^ knotIndex;
|
||||
hashCode = (hashCode * 397) ^ tangentIndex;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SelectionContext : ScriptableObject
|
||||
{
|
||||
static SelectionContext s_Instance;
|
||||
|
||||
public List<SelectableSplineElement> selection = new List<SelectableSplineElement>();
|
||||
public int version;
|
||||
|
||||
public static SelectionContext instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
{
|
||||
s_Instance = CreateInstance<SelectionContext>();
|
||||
s_Instance.hideFlags = HideFlags.HideAndDontSave;
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
SelectionContext()
|
||||
{
|
||||
if (s_Instance == null)
|
||||
s_Instance = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 33dac614537a60e43ac2b6692f9dc39a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,476 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Splines
|
||||
{
|
||||
interface ISplineElement
|
||||
{
|
||||
float3 position { get; set; }
|
||||
float3 localPosition { get; set; }
|
||||
}
|
||||
|
||||
static class SplineSelection
|
||||
{
|
||||
public static event Action changed;
|
||||
|
||||
static readonly List<SelectableSplineElement> s_ElementBuffer = new List<SelectableSplineElement>();
|
||||
static HashSet<Object> s_ObjectBuffer = new HashSet<Object>();
|
||||
|
||||
static SelectionContext context => SelectionContext.instance;
|
||||
static List<SelectableSplineElement> selection => context.selection;
|
||||
|
||||
static int s_SelectionVersion;
|
||||
|
||||
static SplineSelection()
|
||||
{
|
||||
context.version = 0;
|
||||
|
||||
Undo.undoRedoPerformed += OnUndoRedoPerformed;
|
||||
}
|
||||
|
||||
static void OnUndoRedoPerformed()
|
||||
{
|
||||
if (context.version != s_SelectionVersion)
|
||||
{
|
||||
s_SelectionVersion = context.version;
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
if (selection.Count == 0)
|
||||
return;
|
||||
|
||||
IncrementVersion();
|
||||
ClearNoUndo(true);
|
||||
}
|
||||
|
||||
internal static void ClearNoUndo(bool notify)
|
||||
{
|
||||
selection.Clear();
|
||||
if (notify)
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
static bool GetKnotFromElement(SelectableSplineElement element, out EditableKnot knot)
|
||||
{
|
||||
var paths = EditableSplineManager.GetEditableSplines(element.target, false);
|
||||
|
||||
if (paths == null || element.pathIndex >= paths.Count)
|
||||
{
|
||||
knot = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var path = paths[element.pathIndex];
|
||||
if (element.knotIndex < 0 || element.knotIndex >= path.knotCount)
|
||||
{
|
||||
knot = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
knot = path.GetKnot(element.knotIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetTangentFromElement(SelectableSplineElement element, out EditableTangent tangent)
|
||||
{
|
||||
if (!GetKnotFromElement(element, out EditableKnot knot))
|
||||
{
|
||||
tangent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
tangent = knot.GetTangent(element.tangentIndex);
|
||||
return tangent != null;
|
||||
}
|
||||
|
||||
public static void GetSelectedKnots(List<EditableKnot> knots)
|
||||
{
|
||||
knots.Clear();
|
||||
foreach (var element in selection)
|
||||
if (element.isKnot && GetKnotFromElement(element, out EditableKnot knot))
|
||||
knots.Add(knot);
|
||||
}
|
||||
|
||||
public static void GetSelectedKnots(IEnumerable<Object> targets, List<EditableKnot> knots)
|
||||
{
|
||||
knots.Clear();
|
||||
GetSelectedElementsInternal(targets, s_ElementBuffer);
|
||||
foreach (var element in s_ElementBuffer)
|
||||
if (element.isKnot && GetKnotFromElement(element, out EditableKnot knot))
|
||||
knots.Add(knot);
|
||||
}
|
||||
|
||||
public static void GetSelectedTangents(List<EditableTangent> tangents)
|
||||
{
|
||||
tangents.Clear();
|
||||
foreach (var element in selection)
|
||||
if (element.isTangent && GetTangentFromElement(element, out EditableTangent tangent))
|
||||
tangents.Add(tangent);
|
||||
}
|
||||
|
||||
public static void GetSelectedTangents(IEnumerable<Object> targets, List<EditableTangent> tangents)
|
||||
{
|
||||
tangents.Clear();
|
||||
GetSelectedElementsInternal(targets, s_ElementBuffer);
|
||||
foreach (var element in s_ElementBuffer)
|
||||
if (element.isTangent && GetTangentFromElement(element, out EditableTangent tangent))
|
||||
tangents.Add(tangent);
|
||||
}
|
||||
|
||||
public static int count => selection.Count;
|
||||
|
||||
static ISplineElement ToSplineElement(SelectableSplineElement rawElement)
|
||||
{
|
||||
if (rawElement.isKnot)
|
||||
{
|
||||
if (GetKnotFromElement(rawElement, out EditableKnot knot))
|
||||
return knot;
|
||||
}
|
||||
else if (rawElement.isTangent)
|
||||
{
|
||||
if (GetTangentFromElement(rawElement, out EditableTangent tangent))
|
||||
return tangent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ISplineElement GetActiveElement()
|
||||
{
|
||||
//Get first valid element
|
||||
foreach (var rawElement in selection)
|
||||
{
|
||||
var element = ToSplineElement(rawElement);
|
||||
if (element != null)
|
||||
return element;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void GetSelectedElements(ICollection<ISplineElement> elements)
|
||||
{
|
||||
elements.Clear();
|
||||
foreach (var rawElement in selection)
|
||||
{
|
||||
var element = ToSplineElement(rawElement);
|
||||
if (element != null)
|
||||
elements.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetSelectedElements(IEnumerable<Object> targets, ICollection<ISplineElement> elements)
|
||||
{
|
||||
elements.Clear();
|
||||
GetSelectedElementsInternal(targets, s_ElementBuffer);
|
||||
foreach (var rawElement in s_ElementBuffer)
|
||||
{
|
||||
var element = ToSplineElement(rawElement);
|
||||
if (element != null)
|
||||
elements.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
static void GetSelectedElementsInternal(IEnumerable<Object> targets, List<SelectableSplineElement> results)
|
||||
{
|
||||
results.Clear();
|
||||
foreach (var element in selection)
|
||||
foreach(var target in targets)
|
||||
{
|
||||
if(target != null && element.target == target)
|
||||
{
|
||||
results.Add(element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsActiveElement(ISplineElement element)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case EditableKnot knot: return IsActiveElement(knot);
|
||||
case EditableTangent tangent: return IsActiveElement(tangent);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsActiveElement(EditableKnot knot)
|
||||
{
|
||||
return IsActiveElement(new SelectableSplineElement(knot));
|
||||
}
|
||||
|
||||
public static bool IsActiveElement(EditableTangent tangent)
|
||||
{
|
||||
return IsActiveElement(new SelectableSplineElement(tangent));
|
||||
}
|
||||
|
||||
static bool IsActiveElement(SelectableSplineElement element)
|
||||
{
|
||||
return selection.Count > 0 && selection[0].Equals(element);
|
||||
}
|
||||
|
||||
public static void SetActive(ISplineElement element)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case EditableKnot knot:
|
||||
SetActive(knot);
|
||||
break;
|
||||
case EditableTangent tangent:
|
||||
SetActive(tangent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetActive(EditableKnot knot)
|
||||
{
|
||||
SetActiveElement(new SelectableSplineElement(knot));
|
||||
}
|
||||
|
||||
public static void SetActive(EditableTangent tangent)
|
||||
{
|
||||
SetActiveElement(new SelectableSplineElement(tangent));
|
||||
}
|
||||
|
||||
static void SetActiveElement(SelectableSplineElement element)
|
||||
{
|
||||
int index = selection.IndexOf(element);
|
||||
if (index == 0)
|
||||
return;
|
||||
|
||||
IncrementVersion();
|
||||
|
||||
if (index > 0)
|
||||
selection.RemoveAt(index);
|
||||
|
||||
selection.Insert(0, element);
|
||||
|
||||
if(element.target is Component component)
|
||||
{
|
||||
//Set the active unity object so the spline is the first target
|
||||
Object[] unitySelection = Selection.objects;
|
||||
var target = component.gameObject;
|
||||
|
||||
index = Array.IndexOf(unitySelection, target);
|
||||
if(index > 0)
|
||||
{
|
||||
Object prevObj = unitySelection[0];
|
||||
unitySelection[0] = unitySelection[index];
|
||||
unitySelection[index] = prevObj;
|
||||
Selection.objects = unitySelection;
|
||||
}
|
||||
}
|
||||
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
public static void Add(ISplineElement element)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case EditableKnot knot:
|
||||
Add(knot);
|
||||
break;
|
||||
case EditableTangent tangent:
|
||||
Add(tangent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Add(IEnumerable<ISplineElement> elements)
|
||||
{
|
||||
IncrementVersion();
|
||||
|
||||
bool changed = false;
|
||||
foreach (var element in elements)
|
||||
changed |= AddElement(new SelectableSplineElement(element));
|
||||
|
||||
if (changed)
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
public static void Add(EditableKnot knot)
|
||||
{
|
||||
IncrementVersion();
|
||||
|
||||
if (AddElement(new SelectableSplineElement(knot)))
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
public static void Add(IEnumerable<EditableKnot> knots)
|
||||
{
|
||||
IncrementVersion();
|
||||
|
||||
bool changed = false;
|
||||
foreach (var knot in knots)
|
||||
changed |= AddElement(new SelectableSplineElement(knot));
|
||||
|
||||
if (changed)
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
public static void Add(EditableTangent tangent)
|
||||
{
|
||||
IncrementVersion();
|
||||
|
||||
if (AddElement(new SelectableSplineElement(tangent)))
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
public static void Add(IEnumerable<EditableTangent> tangents)
|
||||
{
|
||||
IncrementVersion();
|
||||
|
||||
bool changed = false;
|
||||
foreach (var tangent in tangents)
|
||||
changed |= AddElement(new SelectableSplineElement(tangent));
|
||||
|
||||
if (changed)
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
static bool AddElement(SelectableSplineElement element)
|
||||
{
|
||||
if (!selection.Contains(element))
|
||||
{
|
||||
selection.Insert(0,element);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool Remove(ISplineElement element)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case EditableKnot knot: return Remove(knot);
|
||||
case EditableTangent tangent: return Remove(tangent);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Remove(EditableKnot knot)
|
||||
{
|
||||
IncrementVersion();
|
||||
|
||||
return RemoveElement(new SelectableSplineElement(knot));
|
||||
}
|
||||
|
||||
public static bool Remove(EditableTangent tangent)
|
||||
{
|
||||
IncrementVersion();
|
||||
|
||||
return RemoveElement(new SelectableSplineElement(tangent));
|
||||
}
|
||||
|
||||
static bool RemoveElement(SelectableSplineElement element)
|
||||
{
|
||||
if (selection.Remove(element))
|
||||
{
|
||||
NotifySelectionChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool Contains(ISplineElement element)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case EditableKnot knot: return Contains(knot);
|
||||
case EditableTangent tangent: return Contains(tangent);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Contains(EditableKnot knot)
|
||||
{
|
||||
return ContainsElement(new SelectableSplineElement(knot));
|
||||
}
|
||||
|
||||
public static bool Contains(EditableTangent tangent)
|
||||
{
|
||||
return ContainsElement(new SelectableSplineElement(tangent));
|
||||
}
|
||||
|
||||
static bool ContainsElement(SelectableSplineElement element)
|
||||
{
|
||||
return selection.Contains(element);
|
||||
}
|
||||
|
||||
internal static void UpdateObjectSelection(IEnumerable<Object> targets)
|
||||
{
|
||||
s_ObjectBuffer.Clear();
|
||||
foreach (var target in targets)
|
||||
if (target != null)
|
||||
s_ObjectBuffer.Add(target);
|
||||
|
||||
IncrementVersion();
|
||||
if (selection.RemoveAll(ObjectRemovePredicate) > 0)
|
||||
NotifySelectionChanged();
|
||||
}
|
||||
|
||||
static bool ObjectRemovePredicate(SelectableSplineElement element)
|
||||
{
|
||||
return !s_ObjectBuffer.Contains(element.target);
|
||||
}
|
||||
|
||||
//Used when inserting new elements in spline
|
||||
internal static void MoveAllIndexUpFromIndexToEnd(IEditableSpline spline, int index)
|
||||
{
|
||||
for (var i = 0; i < selection.Count; ++i)
|
||||
{
|
||||
var knot = selection[i];
|
||||
if (knot.IsFromPath(spline))
|
||||
{
|
||||
if (knot.knotIndex >= index)
|
||||
++knot.knotIndex;
|
||||
|
||||
selection[i] = knot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Used when deleting an element in spline
|
||||
internal static void OnKnotRemoved(IEditableSpline spline, int index)
|
||||
{
|
||||
for (var i = selection.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var knot = selection[i];
|
||||
if (knot.IsFromPath(spline))
|
||||
{
|
||||
if (knot.knotIndex == index)
|
||||
selection.RemoveAt(i);
|
||||
else if (knot.knotIndex >= index)
|
||||
{
|
||||
--knot.knotIndex;
|
||||
selection[i] = knot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void IncrementVersion()
|
||||
{
|
||||
Undo.RecordObject(context, "Spline Selection Changed");
|
||||
|
||||
++s_SelectionVersion;
|
||||
++context.version;
|
||||
}
|
||||
|
||||
static void NotifySelectionChanged()
|
||||
{
|
||||
changed?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7529a2acb193d1946b9ce1ecb41dd4d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Add table
Add a link
Reference in a new issue