initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,157 @@
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
#if !UNITY_2022_1_OR_NEWER
using UnityEditor.UIElements;
#endif
namespace UnityEditor.Splines
{
sealed class BezierKnotDrawer : KnotDrawer<BezierEditableKnot>
{
const string k_TangentFoldoutStyle = "tangent-drawer";
readonly TangentModeStrip m_Mode;
readonly FloatField m_InMagnitude;
readonly Vector3Field m_In;
readonly FloatField m_InX;
readonly FloatField m_InY;
readonly FloatField m_InZ;
readonly FloatField m_OutMagnitude;
readonly Vector3Field m_Out;
readonly FloatField m_OutX;
readonly FloatField m_OutY;
readonly FloatField m_OutZ;
public BezierKnotDrawer()
{
Add(m_Mode = new TangentModeStrip());
( m_InMagnitude, m_In ) = CreateTangentFoldout("Tangent In", "TangentIn");
m_InX = m_In.Q<FloatField>("unity-x-input");
m_InY = m_In.Q<FloatField>("unity-y-input");
m_InZ = m_In.Q<FloatField>("unity-z-input");
( m_OutMagnitude, m_Out ) = CreateTangentFoldout("Tangent Out", "TangentOut");
m_OutX = m_Out.Q<FloatField>("unity-x-input");
m_OutY = m_Out.Q<FloatField>("unity-y-input");
m_OutZ = m_Out.Q<FloatField>("unity-z-input");
m_InMagnitude.RegisterValueChangedCallback((evt) =>
{
UpdateTangentMagnitude(target.tangentIn, m_InMagnitude, evt.newValue, -1f);
m_In.SetValueWithoutNotify(target.tangentIn.localPosition);
m_Out.SetValueWithoutNotify(target.tangentOut.localPosition);
m_OutMagnitude.SetValueWithoutNotify(Round(math.length(target.tangentOut.localPosition)));
RoundFloatFieldsValues();
});
m_In.RegisterValueChangedCallback((evt) =>
{
IgnoreKnotCallbacks(true);
target.tangentIn.localPosition = evt.newValue;
IgnoreKnotCallbacks(false);
m_InMagnitude.SetValueWithoutNotify(Round(math.length(target.tangentIn.localPosition)));
});
m_OutMagnitude.RegisterValueChangedCallback((evt) =>
{
UpdateTangentMagnitude(target.tangentOut, m_OutMagnitude, evt.newValue, 1f);
m_Out.SetValueWithoutNotify(target.tangentOut.localPosition);
m_In.SetValueWithoutNotify(target.tangentIn.localPosition);
m_InMagnitude.SetValueWithoutNotify(Round(math.length(target.tangentIn.localPosition)));
RoundFloatFieldsValues();
});
m_Out.RegisterValueChangedCallback((evt) =>
{
IgnoreKnotCallbacks(true);
target.tangentOut.localPosition = evt.newValue;
IgnoreKnotCallbacks(false);
m_OutMagnitude.SetValueWithoutNotify(Round(math.length(target.tangentOut.localPosition)));
});
}
public override void Update()
{
base.Update();
m_Mode.SetElement(target);
m_In.SetValueWithoutNotify(target.tangentIn.localPosition);
m_Out.SetValueWithoutNotify(target.tangentOut.localPosition);
m_InMagnitude.SetValueWithoutNotify(math.length(target.tangentIn.localPosition));
m_OutMagnitude.SetValueWithoutNotify(math.length(target.tangentOut.localPosition));
RoundFloatFieldsValues();
//Disabling edition when using linear tangents
EnableElements(target.mode);
}
void UpdateTangentMagnitude(EditableTangent tangent, FloatField magnitudeField, float value, float directionSign)
{
if (value < 0f)
{
magnitudeField.SetValueWithoutNotify(0f);
value = 0f;
}
var direction = new float3(0, 0, directionSign);
if(math.length(tangent.localPosition) > 0)
direction = math.normalize(tangent.localPosition);
IgnoreKnotCallbacks(true);
tangent.localPosition = value * direction;
IgnoreKnotCallbacks(false);
}
void RoundFloatFieldsValues()
{
m_InMagnitude.SetValueWithoutNotify(Round(m_InMagnitude.value));
m_InX.SetValueWithoutNotify(Round(m_InX.value));
m_InY.SetValueWithoutNotify(Round(m_InY.value));
m_InZ.SetValueWithoutNotify(Round(m_InZ.value));
m_OutMagnitude.SetValueWithoutNotify(Round(m_OutMagnitude.value));
m_OutX.SetValueWithoutNotify(Round(m_OutX.value));
m_OutY.SetValueWithoutNotify(Round(m_OutY.value));
m_OutZ.SetValueWithoutNotify(Round(m_OutZ.value));
}
void EnableElements(BezierEditableKnot.Mode mode)
{
var bezierTangent = mode != BezierEditableKnot.Mode.Linear;
var brokenTangents = mode == BezierEditableKnot.Mode.Broken;
m_InMagnitude.SetEnabled(bezierTangent);
m_OutMagnitude.SetEnabled(bezierTangent);
m_In.SetEnabled(brokenTangents);
m_Out.SetEnabled(brokenTangents);
}
(FloatField,Vector3Field) CreateTangentFoldout(string text, string vect3name)
{
//Create Elements
var foldoutRoot = new VisualElement();
foldoutRoot.AddToClassList(k_TangentFoldoutStyle);
var foldout = new Foldout() { value = false };
var foldoutToggle = foldout.Q<Toggle>();
var magnitude = new FloatField(L10n.Tr(text), 3);
var vector3Field = new Vector3Field() { name = vect3name };
//Build UI Hierarchy
Add(foldoutRoot);
foldoutRoot.Add(foldout);
foldoutToggle.Add(magnitude);
foldout.Add(vector3Field);
return (magnitude, vector3Field);
}
public override void OnTargetSet()
{
m_In.parent.SetEnabled(SplineSelectionUtility.IsSelectable(target.spline, target.index, target.tangentIn));
m_Out.parent.SetEnabled(SplineSelectionUtility.IsSelectable(target.spline, target.index, target.tangentOut));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1dec5e4d3c4dd84aab4dfbb3c8e6b48
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,41 @@
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
interface IElementDrawer
{
void SetTarget(ISplineElement element);
void Update();
void OnTargetSet();
}
abstract class ElementDrawer<T> : VisualElement, IElementDrawer where T : ISplineElement
{
const int k_FloatFieldsDigits = 3;
public T target { get; private set; }
public virtual void Update() {}
public void SetTarget(ISplineElement element)
{
target = (T) element;
OnTargetSet();
}
public static float Round(float value)
{
float mult = Mathf.Pow(10.0f, k_FloatFieldsDigits);
return Mathf.Round(value * mult) / mult;
}
public virtual void OnTargetSet() { }
protected void IgnoreKnotCallbacks(bool ignore)
{
if (parent is ElementInspector inspector)
inspector.ignoreKnotCallbacks = ignore;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95245c7aa2899544b8fe0daa4947e0be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
sealed class ElementInspector : VisualElement, IDisposable
{
static readonly string k_NoSelectionMessage = L10n.Tr("No element selected");
static readonly string k_MultiSelectNoAllowedMessage = L10n.Tr(" - not supported");
readonly Label m_ErrorMessage;
Type m_InspectedType;
EditableKnot m_TargetKnot;
IElementDrawer m_ElementDrawer;
static StyleSheet s_CommonStyleSheet;
static StyleSheet s_ThemeStyleSheet;
bool m_InspectorDirty;
public bool ignoreKnotCallbacks { get; set; }
public ElementInspector()
{
if (s_CommonStyleSheet == null)
s_CommonStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Packages/com.unity.splines/Editor/Stylesheets/SplineInspectorCommon.uss");
if (s_ThemeStyleSheet == null)
s_ThemeStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>($"Packages/com.unity.splines/Editor/Stylesheets/SplineInspector{(EditorGUIUtility.isProSkin ? "Dark" : "Light")}.uss");
styleSheets.Add(s_CommonStyleSheet);
styleSheets.Add(s_ThemeStyleSheet);
m_ErrorMessage = new Label();
Add(m_ErrorMessage);
EditableKnot.knotModified += OnKnotModified;
EditorApplication.update += UpdateIfDirty;
}
public void Dispose()
{
EditableKnot.knotModified -= OnKnotModified;
}
void OnKnotModified(EditableKnot knot)
{
if (!ignoreKnotCallbacks && m_TargetKnot == knot)
m_InspectorDirty = true;
}
void UpdateIfDirty()
{
if(m_InspectorDirty)
{
m_ElementDrawer?.Update();
m_InspectorDirty = false;
}
}
public void SetElement(ISplineElement element, int selectCount)
{
UpdateDrawerForElementType(selectCount > 1 ? null : element?.GetType());
if (selectCount > 1)
ShowErrorMessage(BuildMultiSelectError(selectCount)+k_MultiSelectNoAllowedMessage);
else if (element == null || m_ElementDrawer == null)
ShowErrorMessage(k_NoSelectionMessage);
else
{
if (element is EditableKnot knot)
m_TargetKnot = knot;
else if (element is EditableTangent tangent)
m_TargetKnot = tangent.owner;
HideErrorMessage();
m_ElementDrawer.SetTarget(element);
m_ElementDrawer.Update();
}
}
string BuildMultiSelectError(int selectCount)
{
string message = "(" + selectCount + ") ";
var selectionList = new List<ISplineElement>();
SplineSelection.GetSelectedElements(selectionList);
var isLookingForKnots = selectionList.FirstOrDefault() is EditableKnot;
foreach(var element in selectionList)
{
if(isLookingForKnots && element is EditableKnot)
continue;
if(!isLookingForKnots && element is EditableTangent)
continue;
message += "Elements selected";
return message;
}
message += isLookingForKnots ? "Knots selected" : "Tangents selected";
return message;
}
void ShowErrorMessage(string error)
{
m_ErrorMessage.style.display = DisplayStyle.Flex;
m_ErrorMessage.text = error;
}
void HideErrorMessage()
{
m_ErrorMessage.style.display = DisplayStyle.None;
}
void UpdateDrawerForElementType(Type targetType)
{
if (m_InspectedType == targetType)
return;
if (m_ElementDrawer != null)
((VisualElement)m_ElementDrawer).RemoveFromHierarchy();
if (targetType == null)
m_ElementDrawer = null;
else if (typeof(BezierEditableKnot).IsAssignableFrom(targetType))
m_ElementDrawer = new BezierKnotDrawer();
else if (typeof(EditableKnot).IsAssignableFrom(targetType))
m_ElementDrawer = new KnotDrawer();
else if (typeof(EditableTangent).IsAssignableFrom(targetType))
m_ElementDrawer = new TangentDrawer();
else
m_ElementDrawer = null;
if (m_ElementDrawer != null)
Add((VisualElement)m_ElementDrawer);
m_InspectedType = targetType;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5d8a953f9ef52441bd58f4c1e5853a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,83 @@
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
sealed class KnotDrawer : KnotDrawer<EditableKnot> {}
class KnotDrawer<T> : ElementDrawer<T> where T : EditableKnot
{
readonly Label m_KnotLabel;
readonly Vector3Field m_Position;
readonly FloatField m_PositionX;
readonly FloatField m_PositionY;
readonly FloatField m_PositionZ;
readonly Vector3Field m_Rotation;
readonly FloatField m_RotationX;
readonly FloatField m_RotationY;
readonly FloatField m_RotationZ;
public KnotDrawer()
{
Add(m_KnotLabel = new Label());
m_KnotLabel.style.height = 24;
m_KnotLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
VisualElement row;
Add(row = new VisualElement(){name = "Vector3WithIcon"});
row.style.flexDirection = FlexDirection.Row;
row.Add(new VisualElement(){name = "PositionIcon"});
row.Add(m_Position = new Vector3Field() { name = "Position" });
m_Position.style.flexGrow = 1;
m_PositionX = m_Position.Q<FloatField>("unity-x-input");
m_PositionY = m_Position.Q<FloatField>("unity-y-input");
m_PositionZ = m_Position.Q<FloatField>("unity-z-input");
Add(row = new VisualElement(){name = "Vector3WithIcon"});
row.style.flexDirection = FlexDirection.Row;
row.Add(new VisualElement(){name = "RotationIcon"});
row.Add(m_Rotation = new Vector3Field() { name = "Rotation" });;
m_Rotation.style.flexGrow = 1;
m_RotationX = m_Rotation.Q<FloatField>("unity-x-input");
m_RotationY = m_Rotation.Q<FloatField>("unity-y-input");
m_RotationZ = m_Rotation.Q<FloatField>("unity-z-input");
m_Position.RegisterValueChangedCallback((evt) =>
{
IgnoreKnotCallbacks(true);
target.localPosition = evt.newValue;
IgnoreKnotCallbacks(false);
});
m_Rotation.RegisterValueChangedCallback((evt) =>
{
IgnoreKnotCallbacks(true);
target.localRotation = Quaternion.Euler(evt.newValue);
IgnoreKnotCallbacks(false);
});
}
public override void Update()
{
base.Update();
m_KnotLabel.text = "Knot "+target.index.ToString()+" selected";
m_Position.SetValueWithoutNotify(target.localPosition);
m_Rotation.SetValueWithoutNotify(((Quaternion)target.localRotation).eulerAngles);
RoundFloatFieldsValues();
}
void RoundFloatFieldsValues()
{
m_PositionX.SetValueWithoutNotify(Round(m_PositionX.value));
m_PositionY.SetValueWithoutNotify(Round(m_PositionY.value));
m_PositionZ.SetValueWithoutNotify(Round(m_PositionZ.value));
m_RotationX.SetValueWithoutNotify(Round(m_RotationX.value));
m_RotationY.SetValueWithoutNotify(Round(m_RotationY.value));
m_RotationZ.SetValueWithoutNotify(Round(m_RotationZ.value));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3018d9f76949a7f4c8ad97e48caff165
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
sealed class ReadOnlyField : BaseField<string>
{
readonly Label m_IndexField;
public ReadOnlyField(string label) : base(label, new Label() { name = "ReadOnlyValue" })
{
style.flexDirection = FlexDirection.Row;
m_IndexField = this.Q<Label>("ReadOnlyValue");
m_IndexField.text = value;
m_IndexField.style.unityTextAlign = TextAnchor.MiddleLeft;
}
public override void SetValueWithoutNotify(string newValue)
{
m_IndexField.text = newValue;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37aa8727811c9e04785d9c0e4cafc5d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,125 @@
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
#if !UNITY_2022_1_OR_NEWER
using UnityEditor.UIElements;
#endif
namespace UnityEditor.Splines
{
sealed class TangentDrawer : ElementDrawer<EditableTangent>
{
const string k_TangentDrawerStyle = "tangent-drawer";
const string k_TangentLabelStyle = "tangent-label";
const string k_TangentFillerStyle = "tangent-filler";
readonly Label m_TangentLabel;
readonly TangentModeStrip m_Mode;
FloatField m_Magnitude;
Label m_DirectionLabel;
Vector3Field m_Direction;
FloatField m_DirectionX;
FloatField m_DirectionY;
FloatField m_DirectionZ;
public TangentDrawer()
{
AddToClassList(k_TangentDrawerStyle);
Add(m_TangentLabel = new Label());
m_TangentLabel.style.height = 24;
m_TangentLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
Add(m_Mode = new TangentModeStrip());
CreateTangentFields();
m_Magnitude.RegisterValueChangedCallback((evt) =>
{
UpdateTangentMagnitude(evt.newValue);
m_Direction.SetValueWithoutNotify(target.localPosition);
RoundFloatFieldsValues();
});
m_Direction.RegisterValueChangedCallback((evt) =>
{
IgnoreKnotCallbacks(true);
target.localPosition = evt.newValue;
IgnoreKnotCallbacks(false);
m_Magnitude.SetValueWithoutNotify(Round(math.length(target.localPosition)));
});
}
public override void Update()
{
base.Update();
m_TangentLabel.text = GetTangentLabel();
m_Mode.SetElement(target);
m_Magnitude.SetValueWithoutNotify(math.length(target.localPosition));
m_Direction.SetValueWithoutNotify(target.localPosition);
RoundFloatFieldsValues();
//Disabling edition when using linear, mirrored or continuous tangents
EnableElements(m_Mode.GetMode());
}
void CreateTangentFields()
{
m_Magnitude = new FloatField("Magnitude",6);
m_DirectionLabel = new Label("Direction");
m_DirectionLabel.AddToClassList(k_TangentLabelStyle);
var filler = new VisualElement();
filler.AddToClassList(k_TangentFillerStyle);
m_Direction = new Vector3Field(){name = "direction"};
m_DirectionX = m_Direction.Q<FloatField>("unity-x-input");
m_DirectionY = m_Direction.Q<FloatField>("unity-y-input");
m_DirectionZ = m_Direction.Q<FloatField>("unity-z-input");
//Build UI Hierarchy
Add(m_Magnitude);
Add(m_DirectionLabel);
Add(filler);
filler.Add(m_Direction);
}
string GetTangentLabel()
{
var inOutLabel = target.tangentIndex == 0 ? "In" : "Out";
string label = "Tangent "+inOutLabel+" selected (Knot "+target.owner.index+")";
return label;
}
void UpdateTangentMagnitude(float value)
{
var direction = new float3(0, 0, 1);
if(math.length(target.localPosition) > 0)
direction = math.normalize(target.localPosition);
IgnoreKnotCallbacks(true);
target.localPosition = value * direction;
IgnoreKnotCallbacks(false);
}
void RoundFloatFieldsValues()
{
m_Magnitude.SetValueWithoutNotify(Round(m_Magnitude.value));
m_DirectionX.SetValueWithoutNotify(Round(m_DirectionX.value));
m_DirectionY.SetValueWithoutNotify(Round(m_DirectionY.value));
m_DirectionZ.SetValueWithoutNotify(Round(m_DirectionZ.value));
}
void EnableElements(BezierEditableKnot.Mode mode)
{
var bezierTangent = m_Mode.GetMode() != BezierEditableKnot.Mode.Linear;
var brokenMode = m_Mode.GetMode() == BezierEditableKnot.Mode.Broken;
m_Magnitude.SetEnabled(bezierTangent);
m_DirectionLabel.SetEnabled(brokenMode);
m_Direction.SetEnabled(brokenMode);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d01d6b9007f24984692322edf5415826
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,88 @@
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
sealed class TangentModeStrip : VisualElement
{
readonly GUIContent[] modes = new[]
{
EditorGUIUtility.TrTextContent("Linear", "Linear Tangents:\nTangents are pointing to the previous/next spline knot."),
EditorGUIUtility.TrTextContent("Mirrored", "Mirrored Tangents:\nIf Knot or InTangent is selected, OutTangent will be mirrored on InTangent. Else, InTangent will be mirrored on OutTangent."),
EditorGUIUtility.TrTextContent("Continuous", "Continuous Tangents:\nInTangent and OutTangent are always aligned."),
EditorGUIUtility.TrTextContent("Broken", "Broken Tangents:\nInTangent and OutTangent are dissociated.")
};
readonly ButtonStripField m_ModeStrip;
ISplineElement m_Target;
internal TangentModeStrip()
{
Add(m_ModeStrip = new ButtonStripField() { name = "TangentMode" });
m_ModeStrip.choices = modes;
}
internal BezierEditableKnot.Mode GetMode()
{
return (BezierEditableKnot.Mode)m_ModeStrip.value;
}
internal void SetElement(ISplineElement target)
{
if(m_Target != target)
{
m_Target = target;
BezierEditableKnot knot = null;
if(target is BezierEditableKnot targetedKnot)
knot = targetedKnot;
else if(m_Target is EditableTangent targetedTangent && targetedTangent.owner is BezierEditableKnot tangentOwner)
knot = tangentOwner;
m_ModeStrip.OnValueChanged += ((newMode) => UpdateMode(knot, (BezierEditableKnot.Mode) newMode));
}
if(m_Target is BezierEditableKnot tKnot)
UpdateValue((int)tKnot.mode);
else if(m_Target is EditableTangent tTangent && tTangent.owner is BezierEditableKnot tangentOwner)
UpdateValue((int)tangentOwner.mode);
}
void UpdateMode(BezierEditableKnot knot, BezierEditableKnot.Mode mode)
{
if(knot.mode == mode)
return;
if(mode is BezierEditableKnot.Mode.Mirrored or BezierEditableKnot.Mode.Continuous)
{
// m_Target is the knot "knot", use the InTangent to resolve the new mode
var refTangent = knot.GetTangent(0);
var otherTangent = knot.GetTangent(1);
//Else if target is a tangent, update the values regarding the selected tangent
if(m_Target is EditableTangent { owner: BezierEditableKnot owner } target)
{
refTangent = target;
for(int i = 0; i < owner.tangentCount; ++i)
{
var tangent = owner.GetTangent(i);
if(tangent != target)
otherTangent = tangent;
}
}
if(mode == BezierEditableKnot.Mode.Mirrored)
otherTangent.SetLocalPositionNoNotify(-refTangent.localPosition);
else //Continuous mode
otherTangent.SetLocalPositionNoNotify(-math.normalize(refTangent.localPosition) * math.length(otherTangent.localPosition));
}
knot.SetMode(mode);
}
internal void UpdateValue(int modeValue)
{
m_ModeStrip.SetValueWithoutNotify(modeValue);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c74ddef7e6e74f529a51b5ad71552fbd
timeCreated: 1637605071