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,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.Splines.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Splines.Editor.Debug")]

View file

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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e88477d7eedfb4ca8b5d8912575efa76
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,230 @@
using System;
using UnityEngine.Splines;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
[CustomEditor(typeof(SplineAnimate))]
[CanEditMultipleObjects]
class SplineAnimateEditor : UnityEditor.Editor
{
VisualElement m_Root;
Button m_PlayButton;
Slider m_ProgressSlider;
FloatField m_ElapsedTimeField;
EnumField m_ObjectForwardField;
EnumField m_ObjectUpField;
SerializedProperty m_TargetProperty;
SerializedProperty m_MethodProperty;
SerializedProperty m_DurationProperty;
SerializedProperty m_SpeedProperty;
SerializedProperty m_ObjectForwardProperty;
SerializedProperty m_ObjectUpProperty;
SerializedObject m_TransformSO;
SplineAnimate m_SplineAnimate;
const string k_UxmlPath = "Packages/com.unity.splines/Editor/Resources/UI/UXML/splineanimate-inspector.uxml";
static VisualTreeAsset s_TreeAsset;
static StyleSheet s_ThemeStyleSheet;
void OnEnable()
{
m_SplineAnimate = target as SplineAnimate;
m_SplineAnimate.onUpdated += OnSplineAnimateUpdated;
m_TargetProperty = serializedObject.FindProperty("m_Target");
m_MethodProperty = serializedObject.FindProperty("m_Method");
m_DurationProperty = serializedObject.FindProperty("m_Duration");
m_SpeedProperty = serializedObject.FindProperty("m_MaxSpeed");
m_ObjectForwardProperty = serializedObject.FindProperty("m_ObjectForwardAxis");
m_ObjectUpProperty = serializedObject.FindProperty("m_ObjectUpAxis");
m_TransformSO = new SerializedObject(m_SplineAnimate.transform);
EditorApplication.update += OnEditorUpdate;
}
void OnDisable()
{
if (m_SplineAnimate != null && m_SplineAnimate.splineContainer != null)
{
if (!EditorApplication.isPlaying)
m_SplineAnimate.Restart(false);
m_SplineAnimate.onUpdated -= OnSplineAnimateUpdated;
}
EditorApplication.update -= OnEditorUpdate;
}
void OnEditorUpdate()
{
if (m_SplineAnimate == null || m_SplineAnimate.splineContainer == null)
return;
if (m_SplineAnimate.isPlaying && !EditorApplication.isPlaying)
m_SplineAnimate.Update();
RefreshProgressFields();
}
public override VisualElement CreateInspectorGUI()
{
m_Root = new VisualElement();
if (s_TreeAsset == null)
s_TreeAsset = (VisualTreeAsset)AssetDatabase.LoadAssetAtPath(k_UxmlPath, typeof(VisualTreeAsset));
s_TreeAsset.CloneTree(m_Root);
if (s_ThemeStyleSheet == null)
s_ThemeStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>($"Packages/com.unity.splines/Editor/Stylesheets/SplineAnimateInspector{(EditorGUIUtility.isProSkin ? "Dark" : "Light")}.uss");
m_Root.styleSheets.Add(s_ThemeStyleSheet);
var splineField = m_Root.Q<PropertyField>("spline-container");
splineField.RegisterValueChangeCallback((_) => { m_SplineAnimate.splineContainer = m_TargetProperty.objectReferenceValue as SplineContainer; });
var methodField = m_Root.Q<PropertyField>("method");
methodField.RegisterValueChangeCallback((_) => { RefreshMethodParamFields((SplineAnimate.Method)m_MethodProperty.enumValueIndex); });
RefreshMethodParamFields((SplineAnimate.Method)m_MethodProperty.enumValueIndex);
var durationField = m_Root.Q<PropertyField>("duration");
durationField.RegisterValueChangeCallback((_) => { m_SplineAnimate.duration = m_DurationProperty.floatValue; });
var speedField = m_Root.Q<PropertyField>("max-speed");
speedField.RegisterValueChangeCallback((_) => { m_SplineAnimate.maxSpeed = m_SpeedProperty.floatValue; });
m_ObjectForwardField = m_Root.Q<EnumField>("object-forward");
m_ObjectForwardField.RegisterValueChangedCallback((evt) => OnObjectAxisFieldChange(evt, m_ObjectForwardProperty, m_ObjectUpProperty));
m_ObjectUpField = m_Root.Q<EnumField>("object-up");
m_ObjectUpField.RegisterValueChangedCallback((evt) => OnObjectAxisFieldChange(evt, m_ObjectUpProperty, m_ObjectForwardProperty));
var playButton = m_Root.Q<Button>("play");
playButton.clicked += OnPlayClicked;
var pauseButton = m_Root.Q<Button>("pause");
pauseButton.clicked += OnPauseClicked;
var resetButton = m_Root.Q<Button>("reset");
resetButton.clicked += OnResetClicked;
m_ProgressSlider = m_Root.Q<Slider>("normalized-progress");
m_ProgressSlider.RegisterValueChangedCallback((evt) => OnProgressSliderChange(evt.newValue));
m_ElapsedTimeField = m_Root.Q<FloatField>("elapsed-time");
m_ElapsedTimeField.RegisterValueChangedCallback((evt) => OnElapsedTimeFieldChange(evt.newValue));
return m_Root;
}
void RefreshMethodParamFields(SplineAnimate.Method method)
{
var durationField = m_Root.Q<PropertyField>("duration");
var maxSpeedField = m_Root.Q<PropertyField>("max-speed");
if (method == (int) SplineAnimate.Method.Time)
{
durationField.style.display = DisplayStyle.Flex;
maxSpeedField.style.display = DisplayStyle.None;
}
else
{
durationField.style.display = DisplayStyle.None;
maxSpeedField.style.display = DisplayStyle.Flex;
}
}
void RefreshProgressFields()
{
if (m_ProgressSlider == null || m_ElapsedTimeField == null)
return;
m_ProgressSlider.SetValueWithoutNotify(m_SplineAnimate.GetLoopInterpolation());
m_ElapsedTimeField.SetValueWithoutNotify(m_SplineAnimate.elapsedTime);
}
void OnProgressSliderChange(float progress)
{
m_SplineAnimate.Pause();
m_SplineAnimate.normalizedTime = progress;
RefreshProgressFields();
}
void OnElapsedTimeFieldChange(float elapsedTime)
{
m_SplineAnimate.Pause();
m_SplineAnimate.elapsedTime = elapsedTime;
RefreshProgressFields();
}
void OnObjectAxisFieldChange(ChangeEvent<Enum> changeEvent, SerializedProperty axisProp, SerializedProperty otherAxisProp)
{
if (changeEvent.newValue == null)
return;
var newValue = (SplineAnimate.AlignAxis)changeEvent.newValue;
var previousValue = (SplineAnimate.AlignAxis)changeEvent.previousValue;
// Swap axes if the picked value matches that of the other axis field
if (newValue == (SplineAnimate.AlignAxis)otherAxisProp.enumValueIndex)
{
otherAxisProp.enumValueIndex = (int)previousValue;
serializedObject.ApplyModifiedProperties();
}
// Prevent the user from configuring object's forward and up as opposite axes
if (((int) newValue) % 3 == otherAxisProp.enumValueIndex % 3)
{
axisProp.enumValueIndex = (int)previousValue;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
}
void OnPlayClicked()
{
if (!m_SplineAnimate.isPlaying)
{
if (m_SplineAnimate.normalizedTime == 1f)
m_SplineAnimate.Restart(true);
else
m_SplineAnimate.Play();
}
}
void OnPauseClicked()
{
m_SplineAnimate.Pause();
}
void OnResetClicked()
{
m_SplineAnimate.Restart(false);
RefreshProgressFields();
}
void OnSplineAnimateUpdated(Vector3 position, Quaternion rotation)
{
if (!EditorApplication.isPlaying)
{
m_TransformSO.Update();
var localPosition = position;
var localRotation = rotation;
if (m_SplineAnimate.transform.parent != null)
{
localPosition = m_SplineAnimate.transform.parent.worldToLocalMatrix.MultiplyPoint3x4(position);
localRotation = Quaternion.Inverse(m_SplineAnimate.transform.parent.rotation) * localRotation;
}
m_TransformSO.FindProperty("m_LocalPosition").vector3Value = localPosition;
m_TransformSO.FindProperty("m_LocalRotation").quaternionValue = localRotation;
m_TransformSO.ApplyModifiedProperties();
}
}
}
}

View file

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

View file

@ -0,0 +1,51 @@
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
[CustomEditor(typeof(SplineContainer))]
class SplineContainerEditor : UnityEditor.Editor
{
SerializedProperty m_SplineProperty;
static GUIStyle s_HelpLabelStyle;
static GUIContent s_HelpLabelContent;
static GUIContent s_HelpLabelContentIcon;
const string k_ComponentMessage = "Use the <b>Spline Edit Mode</b> in the <b>Scene Tools Overlay</b> to edit this Spline.";
const string k_HelpBoxIconPath = "Packages/com.unity.splines/Editor/Resources/Icons/SplineContext.png";
public void OnEnable()
{
m_SplineProperty = serializedObject.FindProperty("m_Spline");
if(s_HelpLabelContent == null)
s_HelpLabelContent = EditorGUIUtility.TrTextContent(k_ComponentMessage);
if(s_HelpLabelContentIcon == null)
s_HelpLabelContentIcon = EditorGUIUtility.TrIconContent(k_HelpBoxIconPath);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
if (s_HelpLabelStyle == null)
{
s_HelpLabelStyle = new GUIStyle(EditorStyles.helpBox);
s_HelpLabelStyle.padding = new RectOffset(10, 10, 10, 10);
}
EditorGUILayout.BeginHorizontal(s_HelpLabelStyle);
EditorGUILayout.LabelField(s_HelpLabelContentIcon, GUILayout.Width(20), GUILayout.ExpandHeight(true));
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(s_HelpLabelContent, new GUIStyle(EditorStyles.label){richText = true, wordWrap = true});
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(m_SplineProperty);
serializedObject.ApplyModifiedProperties();
}
}
}

View file

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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6042ea3ccf1fb5c45b533a9d67e956b7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Splines
{
static class PathGUIControls
{
internal static readonly List<Vector3> pointsBuffer = new List<Vector3>();
public static void MultiEditVector3Field(GUIContent label, IList<Vector3> points)
{
if (!EditorGUIUtility.wideMode)
GUILayout.Label(label);
EditorGUILayout.BeginHorizontal();
if (EditorGUIUtility.wideMode)
EditorGUILayout.PrefixLabel(label);
var prevMixedValue = EditorGUI.showMixedValue;
var result = GetMultiEditValue(points, out Vector3 value);
EditorGUI.showMixedValue = result.xMixed;
EditorGUI.BeginChangeCheck();
GUILayout.Label("X");
float x = EditorGUILayout.FloatField(value.x);
if (EditorGUI.EndChangeCheck())
{
for (int i = 0; i < points.Count; ++i)
{
var oldValue = points[i];
oldValue.x = x;
points[i] = oldValue;
}
GUI.changed = true;
}
EditorGUI.showMixedValue = result.yMixed;
EditorGUI.BeginChangeCheck();
GUILayout.Label("Y");
float y = EditorGUILayout.FloatField(value.y);
if (EditorGUI.EndChangeCheck())
{
for (int i = 0; i < points.Count; ++i)
{
var oldValue = points[i];
oldValue.y = y;
points[i] = oldValue;
}
GUI.changed = true;
}
EditorGUI.showMixedValue = result.zMixed;
EditorGUI.BeginChangeCheck();
GUILayout.Label("Z");
float z = EditorGUILayout.FloatField(value.z);
if (EditorGUI.EndChangeCheck())
{
for (int i = 0; i < points.Count; ++i)
{
var oldValue = points[i];
oldValue.z = z;
points[i] = oldValue;
}
GUI.changed = true;
}
EditorGUILayout.EndHorizontal();
EditorGUI.showMixedValue = prevMixedValue;
}
//Returns true if the value has multiple values
static (bool xMixed, bool yMixed, bool zMixed) GetMultiEditValue(IList<Vector3> points, out Vector3 value)
{
value = points[0];
bool xMixed = false;
bool yMixed = false;
bool zMixed = false;
for (int i = 1; i < points.Count; ++i)
{
Vector3 point = points[i];
xMixed |= !Mathf.Approximately(value.x, point.x);
yMixed |= !Mathf.Approximately(value.y, point.y);
zMixed |= !Mathf.Approximately(value.z, point.z);
if (xMixed && yMixed && zMixed)
break;
}
return (xMixed, yMixed, zMixed);
}
/// <summary>
/// Get the mixed value for a field
/// </summary>
/// <typeparam name="T">The target type</typeparam>
/// <param name="values">A list of all the values</param>
/// <param name="value">The value that should be used for the field</param>
/// <returns>True if has mixed values</returns>
public static bool GetMixedValue<T>(IList<T> values, out T value)
where T : IEquatable<T>
{
value = values[0];
for (int i = 1; i < values.Count; ++i)
{
if (!value.Equals(values[i]))
return true;
}
return false;
}
}
}

View file

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

View file

@ -0,0 +1,260 @@
using System;
using System.Reflection;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
static class SplineDataHandlesDrawer
{
/// <summary>
/// LabelType used to define if label must be displayed along the handle and how it should be formatted
/// </summary>
internal enum LabelType
{
None,
Index,
Time
};
const float k_HandleSize = 0.15f;
const int k_PickRes = 2;
internal static void InitCustomHandles<T>(
SplineData<T> splineData,
ISplineDataHandle drawerInstance)
{
var ids = new int[splineData.Count];
for(int kfIndex = 0; kfIndex < splineData.Count; kfIndex++)
ids[kfIndex] = GUIUtility.GetControlID(FocusType.Passive);
( (SplineDataHandle<T>)drawerInstance ).m_ControlIDs = ids;
}
internal static void DrawCustomHandles<T>(
SplineData<T> splineData,
Component component,
Spline spline,
Matrix4x4 localToWorld,
Color color,
ISplineDataHandle drawerInstance,
MethodInfo splineDataDrawMethodInfo,
MethodInfo keyframeDrawMethodInfo)
{
if(splineData.Count == 0)
return;
Undo.RecordObject(component, "Modifying Spline Data Points");
//Invoke if existing the custom drawer for the whole spline
splineDataDrawMethodInfo?.Invoke(drawerInstance,
new object[]
{
splineData,
spline,
localToWorld,
color
});
if (keyframeDrawMethodInfo == null)
return;
var native = new NativeSpline(spline, localToWorld);
var ids = ( (SplineDataHandle<T>)drawerInstance ).controlIDs;
for(int splineDataIndex = 0; splineDataIndex < splineData.Count; splineDataIndex++)
{
var keyframe = splineData[splineDataIndex];
var normalizedT = SplineUtility.GetNormalizedInterpolation(native, keyframe.Index, splineData.PathIndexUnit);
native.Evaluate(normalizedT, out float3 dataPosition, out float3 dataDirection, out float3 dataUp);
using(new Handles.DrawingScope(color))
{
//Invoke if existing the custom drawer for each keyframe
keyframeDrawMethodInfo?.Invoke(drawerInstance,
new object[]
{
ids[splineDataIndex],
(Vector3)dataPosition,
(Vector3)dataDirection,
(Vector3)dataUp,
splineData,
splineDataIndex
});
}
}
native.Dispose();
}
internal static void DrawSplineDataHandles<T>(
SplineData<T> splineData,
Component component,
Spline spline,
Matrix4x4 localToWorld,
Color color,
LabelType labelType)
{
Undo.RecordObject(component, "Modifying Spline Data Points");
using var native = new NativeSpline(spline, localToWorld);
using(new Handles.DrawingScope(color))
{
for(int keyframeIndex = 0; keyframeIndex < splineData.Count; keyframeIndex++)
{
var keyframe = splineData[keyframeIndex];
var inUse = SplineDataHandle(splineData, keyframe, native, labelType, keyframeIndex, out float time);
if(inUse)
{
keyframe.Index = time;
splineData.SetDataPointNoSort(keyframeIndex, keyframe);
//OnMouseUp event
if(GUIUtility.hotControl == 0)
splineData.SortIfNecessary();
}
}
}
}
internal static bool SplineDataHandle<T>(
SplineData<T> splineData,
IDataPoint dataPoint,
NativeSpline spline,
LabelType labelType,
int keyframeIndex,
out float newTime)
{
int id = GUIUtility.GetControlID(FocusType.Passive);
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(id);
var normalizedT = SplineUtility.GetNormalizedInterpolation(spline, dataPoint.Index, splineData.PathIndexUnit);
var dataPosition = SplineUtility.EvaluatePosition(spline, normalizedT);
switch (eventType)
{
case EventType.Layout:
{
if(!Tools.viewToolActive)
{
var dist = HandleUtility.DistanceToCircle(dataPosition, k_HandleSize * HandleUtility.GetHandleSize(dataPosition));
HandleUtility.AddControl(id, dist);
}
break;
}
case EventType.Repaint:
DrawSplineDataHandle(dataPosition, id);
DrawSplineDataLabel(dataPosition, labelType, dataPoint, keyframeIndex);
break;
case EventType.MouseDown:
if (evt.button == 0
&& HandleUtility.nearestControl == id
&& GUIUtility.hotControl == 0)
{
GUIUtility.hotControl = id;
evt.Use();
newTime = GetClosestSplineDataT(spline, splineData);
return true;
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
evt.Use();
newTime = GetClosestSplineDataT(spline, splineData);
return true;
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
evt.Use();
if(evt.button == 0)
{
GUIUtility.hotControl = 0;
newTime = GetClosestSplineDataT(spline, splineData);
return true;
}
}
break;
case EventType.MouseMove:
HandleUtility.Repaint();
break;
}
newTime = dataPoint.Index;
return false;
}
static void DrawSplineDataHandle(Vector3 position, int controlID)
{
var handleColor = Handles.color;
if(controlID == GUIUtility.hotControl)
handleColor = Handles.selectedColor;
else if(GUIUtility.hotControl == 0 && controlID == HandleUtility.nearestControl)
handleColor = Handles.preselectionColor;
// to avoid affecting the sphere dimensions with the handles matrix, we'll just use the position and reset
// the matrix to identity when drawing.
position = Handles.matrix * position;
using(new Handles.DrawingScope(handleColor, Matrix4x4.identity))
{
Handles.SphereHandleCap(
controlID,
position,
Quaternion.identity,
k_HandleSize * HandleUtility.GetHandleSize(position),
EventType.Repaint
);
}
}
static void DrawSplineDataLabel(Vector3 position, LabelType labelType, IDataPoint dataPoint, int keyframeIndex)
{
if(labelType == LabelType.None)
return;
float labelVal = dataPoint.Index;
if(labelType == LabelType.Index && keyframeIndex >= 0)
labelVal = keyframeIndex;
var label = ( Mathf.RoundToInt(labelVal * 100) / 100f ).ToString();
label = labelType == LabelType.Index ? "[" + label + "]" : "t: "+label;
Handles.Label(position - 0.1f * Vector3.up, label);
}
// Spline must be in world space
static float GetClosestSplineDataT<T>(NativeSpline spline, SplineData<T> splineData)
{
var evt = Event.current;
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
SplineUtility.GetNearestPoint(spline,
ray,
out float3 _,
out float t,
k_PickRes);
var time = SplineUtility.ConvertIndexUnit(
spline,
t,
PathIndexUnit.Normalized,
splineData.PathIndexUnit);
return time;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 54bce7290a9e479889adc3e8c822798e
timeCreated: 1625512898

View file

@ -0,0 +1,216 @@
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Splines
{
class SplineElementRectSelector
{
enum Mode
{
None,
Replace,
Add,
Subtract
}
static class Styles
{
public static readonly GUIStyle selectionRect = GUI.skin.FindStyle("selectionRect");
}
Rect m_Rect;
Vector2 m_StartPos;
Mode m_Mode;
Mode m_InitialMode;
static readonly HashSet<ISplineElement> s_SplineElementsCompareSet = new HashSet<ISplineElement>();
static readonly List<ISplineElement> s_SplineElementsBuffer = new List<ISplineElement>();
static readonly HashSet<ISplineElement> s_PreRectSelectionElements = new HashSet<ISplineElement>();
public void OnGUI(IReadOnlyList<IEditableSpline> paths)
{
int id = GUIUtility.GetControlID(FocusType.Passive);
Event evt = Event.current;
switch (evt.GetTypeForControl(id))
{
case EventType.Layout:
if (!Tools.viewToolActive)
{
HandleUtility.AddDefaultControl(id);
if (m_Mode != Mode.None)
{
// If we've started rect select in Add or Subtract modes, then if we were in a Replace
// mode just before (i.e. the shift or action has been released temporarily),
// we need to bring back the pre rect selection elements into current selection.
if (m_InitialMode != Mode.Replace && RefreshSelectionMode())
{
SplineSelection.Clear();
s_SplineElementsCompareSet.Clear();
if (m_Mode != Mode.Replace)
{
foreach (var element in s_PreRectSelectionElements)
SplineSelection.Add(element);
}
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
UpdateSelection(m_Rect, paths);
}
}
}
break;
case EventType.Repaint:
if (GUIUtility.hotControl == id && m_Rect.size != Vector2.zero)
{
Handles.BeginGUI();
Styles.selectionRect.Draw(m_Rect, GUIContent.none, false, false, false, false);
Handles.EndGUI();
}
break;
case EventType.MouseDown:
if (HandleUtility.nearestControl == id && evt.button == 0)
{
m_StartPos = evt.mousePosition;
m_Rect = new Rect(Vector3.zero, Vector2.zero);
BeginSelection(paths);
GUIUtility.hotControl = id;
evt.Use();
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
evt.Use();
UpdateSelection(m_Rect, paths);
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
GUIUtility.hotControl = 0;
evt.Use();
EndSelection(m_Rect, paths);
}
break;
}
}
protected virtual void BeginSelection(IReadOnlyList<IEditableSpline> paths)
{
RefreshSelectionMode();
m_InitialMode = m_Mode;
s_SplineElementsCompareSet.Clear();
s_SplineElementsBuffer.Clear();
if (m_Mode == Mode.Replace)
{
SplineSelection.Clear();
s_PreRectSelectionElements.Clear();
}
else
SplineSelection.GetSelectedElements(s_PreRectSelectionElements);
}
protected virtual void UpdateSelection(Rect rect, IReadOnlyList<IEditableSpline> paths)
{
//Get all elements in rect
s_SplineElementsBuffer.Clear();
for (int i = 0; i < paths.Count; ++i)
{
IEditableSpline spline = paths[i];
for (int j = 0; j < spline.knotCount; ++j)
GetElementSelection(rect, spline, j, s_SplineElementsBuffer);
}
foreach (var splineElement in s_SplineElementsBuffer)
{
//Compare current frame buffer with last frame's to find new additions/removals
var wasInRectLastFrame = s_SplineElementsCompareSet.Remove(splineElement);
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
{
var canAdd = m_Mode == Mode.Replace ? true : !s_PreRectSelectionElements.Contains(splineElement);
if (!wasInRectLastFrame && canAdd)
SplineSelection.Add(splineElement);
}
else if (m_Mode == Mode.Subtract && !wasInRectLastFrame)
{
SplineSelection.Remove(splineElement);
}
}
//Remaining spline elements from last frame are removed from selection (or added if mode is subtract)
foreach (var splineElement in s_SplineElementsCompareSet)
{
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
{
// If we're in Add mode, don't remove elements that were in select prior to rect selection
if (m_Mode == Mode.Add && s_PreRectSelectionElements.Contains(splineElement))
continue;
SplineSelection.Remove(splineElement);
}
else if (m_Mode == Mode.Subtract && s_PreRectSelectionElements.Contains(splineElement))
SplineSelection.Add(splineElement);
}
//Move current elements buffer to hash set for next frame compare
s_SplineElementsCompareSet.Clear();
foreach (var splineElement in s_SplineElementsBuffer)
s_SplineElementsCompareSet.Add(splineElement);
}
bool RefreshSelectionMode()
{
var modeBefore = m_Mode;
if (Event.current.shift)
m_Mode = Mode.Add;
else if (EditorGUI.actionKey)
m_Mode = Mode.Subtract;
else
m_Mode = Mode.Replace;
// Return true if the mode has changed
return m_Mode != modeBefore;
}
void GetElementSelection(Rect rect, IEditableSpline spline, int index, List<ISplineElement> results)
{
var knot = spline.GetKnot(index);
Vector3 screenSpace = HandleUtility.WorldToGUIPointWithDepth(knot.position);
if (screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(knot);
for(int tangentIndex = 0; tangentIndex < knot.tangentCount; tangentIndex++)
{
var tangent = knot.GetTangent(tangentIndex);
screenSpace = HandleUtility.WorldToGUIPointWithDepth(tangent.position);
if (SplineSelectionUtility.IsSelectable(spline, index, tangent) && screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(tangent);
}
}
protected virtual void EndSelection(Rect rect, IReadOnlyList<IEditableSpline> paths)
{
m_Mode = m_InitialMode = Mode.None;
}
static Rect GetRectFromPoints(Vector2 a, Vector2 b)
{
Vector2 min = new Vector2(Mathf.Min(a.x, b.x), Mathf.Min(a.y, b.y));
Vector2 max = new Vector2(Mathf.Max(a.x, b.x), Mathf.Max(a.y, b.y));
return new Rect(min, max - min);
}
}
}

View file

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

View file

@ -0,0 +1,613 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.SettingsManagement;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Splines;
using Unity.Mathematics;
namespace UnityEditor.Splines
{
static class SplineHandles
{
[UserSetting]
internal static UserSetting<Color> s_LineNormalFrontColor = new UserSetting<Color>(PathSettings.instance, "Handles.CurveNormalInFrontColor", Color.white, SettingsScope.User);
[UserSetting]
internal static UserSetting<Color> s_LineNormalBehindColor = new UserSetting<Color>(PathSettings.instance, "Handles.CurveNormalBehindColor", new Color(0.98f, 0.62f, 0.62f, 0.4f), SettingsScope.User);
[UserSetting]
internal static UserSetting<Color> s_KnotColor = new UserSetting<Color>(PathSettings.instance, "Handles.KnotDefaultColor", new Color(.4f, 1f, .95f, 1f), SettingsScope.User);
[UserSetting]
internal static UserSetting<Color> s_TangentColor = new UserSetting<Color>(PathSettings.instance, "Handles.TangentDefaultColor", Color.black, SettingsScope.User);
[UserSettingBlock("Handles")]
static void HandleColorPreferences(string searchContext)
{
s_LineNormalFrontColor.value = SettingsGUILayout.SettingsColorField("Curve Color", s_LineNormalFrontColor, searchContext);
s_LineNormalBehindColor.value = SettingsGUILayout.SettingsColorField("Curve Color Behind Surface", s_LineNormalBehindColor, searchContext);
s_KnotColor.value = SettingsGUILayout.SettingsColorField("Knot Color", s_KnotColor, searchContext);
s_TangentColor.value = SettingsGUILayout.SettingsColorField("Tangent Color", s_TangentColor, searchContext);
}
const float k_SizeFactor = 0.15f;
const float k_PickingDistance = 8f;
const float k_HandleWidthDefault = 2f;
const float k_HandleWidthHover = 4f;
const float k_KnotDiscRadiusFactorDefault = 0.06f;
const float k_KnotDiscRadiusFactorHover = 0.07f;
const float k_KnotDiscRadiusFactorSelected = 0.085f;
const float k_KnotRotDiscRadius = 0.18f;
const float k_KnotRotDiscWidthDefault = 1.5f;
const float k_KnotRotDiscWidthHover = 3f;
const float k_KnotRotDiscWidthSelected = 4f;
const float k_TangentLineWidthDefault = 2f;
const float k_TangentLineWidthHover = 3.5f;
const float k_TangentLineWidthSelected = 4.5f;
const float k_TangentStartOffsetFromKnot = 0.22f;
const float k_tangentEndOffsetFromHandle = 0.11f;
const float k_AliasedLineSizeMultiplier = 0.5f;
const int k_SegmentCount = 30;
const float k_CurveLineWidth = 5f;
const float k_PreviewCurveOpacity = 0.5f;
const string k_TangentLineAATexPath = "Textures/TangentLineAATex";
static Texture2D s_ThickTangentLineAATex = Resources.Load<Texture2D>(k_TangentLineAATexPath);
static readonly Vector3[] s_CurveSegmentsBuffer = new Vector3[k_SegmentCount + 1];
static readonly Vector3[] s_SegmentBuffer = new Vector3[2];
static readonly Vector3[] s_AAWireDiscBuffer = new Vector3[18];
static ISplineElement s_LastHoveredTangent;
static int s_LastHoveredTangentID;
static List<int> s_ElementChildIDs = new List<int>();
internal static void DrawSplineHandles(IReadOnlyList<IEditableSpline> paths, SplineHandlesOptions options)
{
for (int i = 0; i < paths.Count; ++i)
{
DrawSplineHandles(paths[i], options);
}
}
internal static bool DrawSplineHandles(IEditableSpline spline, SplineHandlesOptions options, bool activeSpline = true)
{
int lastIndex = spline.closed ? spline.knotCount - 1 : spline.knotCount - 2; //If the spline isn't closed, skip the last index of the spline
var isInsertingKnots = HasOption(options, SplineHandlesOptions.KnotInsert);
int[] curveIDs = new int[0];
if(isInsertingKnots && lastIndex+1>=0)
{
curveIDs = new int[lastIndex+1];
for (int idIndex = 0; idIndex < lastIndex+1; ++idIndex)
curveIDs[idIndex] = GUIUtility.GetControlID(FocusType.Passive);
}
activeSpline = curveIDs.Contains(HandleUtility.nearestControl) || activeSpline;
for (int knotIndex = 0; knotIndex <= lastIndex; ++knotIndex)
{
var curve = new CurveData(spline, knotIndex);
if (isInsertingKnots)
CurveHandleWithKnotInsert(curve, curveIDs[knotIndex], activeSpline);
else
DrawCurve(curve);
}
var drawHandlesAsActive = curveIDs.Contains(HandleUtility.nearestControl) || activeSpline;
for (int knotIndex = 0; knotIndex < spline.knotCount; ++knotIndex)
{
var knot = spline.GetKnot(knotIndex);
if (HasOption(options, SplineHandlesOptions.ShowTangents))
{
for (int tangentIndex = 0; tangentIndex < knot.tangentCount; ++tangentIndex)
{
//Not drawing unused tangents
if (!spline.closed && ((knotIndex == 0 && tangentIndex == 0) ||
(knotIndex != 0 && knotIndex + 1 == spline.knotCount && tangentIndex + 1 == knot.tangentCount)))
continue;
var tangent = knot.GetTangent(tangentIndex);
if (HasOption(options, SplineHandlesOptions.SelectableTangents))
{
var tangentHandlelID = SelectionHandle(tangent);
s_ElementChildIDs.Add(tangentHandlelID);
}
else
DrawTangentHandle(tangent, -1, drawHandlesAsActive);
}
}
if (HasOption(options, SplineHandlesOptions.SelectableKnots))
SelectionHandle(knot);
else
DrawKnotHandle(knot, null, drawHandlesAsActive);
s_ElementChildIDs.Clear();
}
if (s_LastHoveredTangent != null && Event.current.GetTypeForControl(s_LastHoveredTangentID) == EventType.Repaint)
s_LastHoveredTangent = null;
return activeSpline;
}
static bool HasOption(SplineHandlesOptions options, SplineHandlesOptions target)
{
return (options & target) == target;
}
internal static int SelectionHandle(ISplineElement element)
{
int id = GUIUtility.GetControlID(FocusType.Passive);
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(id);
switch (eventType)
{
case EventType.Layout:
if (!Tools.viewToolActive)
{
HandleUtility.AddControl(id, SplineHandleUtility.DistanceToCircle(element.position, k_PickingDistance));
if (element is EditableTangent)
{
if (HandleUtility.nearestControl == id)
{
s_LastHoveredTangent = element;
s_LastHoveredTangentID = id;
}
}
}
break;
case EventType.MouseDown:
if (HandleUtility.nearestControl == id)
{
//Clicking a knot selects it
if (evt.button != 0)
break;
GUIUtility.hotControl = id;
evt.Use();
//Add/Remove from knotSelection
if (EditorGUI.actionKey || evt.modifiers == EventModifiers.Shift)
{
if (SplineSelection.Contains(element))
SplineSelection.Remove(element);
else
SplineSelection.Add(element);
}
else
{
SplineSelection.Clear();
SplineSelection.Add(element);
}
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
GUIUtility.hotControl = 0;
evt.Use();
}
break;
case EventType.MouseMove:
if (id == HandleUtility.nearestControl)
HandleUtility.Repaint();
break;
case EventType.Repaint:
switch (element)
{
case EditableKnot knot:
DrawKnotHandle(knot, id, s_ElementChildIDs);
break;
case EditableTangent tangent:
DrawTangentHandle(tangent, id);
break;
}
break;
}
return id;
}
internal static bool ButtonHandle(int controlID, EditableKnot knot, bool active)
{
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(controlID);
var position = knot.position;
switch (eventType)
{
case EventType.Layout:
{
if(!Tools.viewToolActive)
HandleUtility.AddControl(controlID, SplineHandleUtility.DistanceToKnot(position));
break;
}
case EventType.Repaint:
DrawKnotHandle(knot, controlID, null, active);
break;
case EventType.MouseDown:
if (HandleUtility.nearestControl == controlID)
{
//Clicking a knot selects it
if (evt.button != 0)
break;
GUIUtility.hotControl = controlID;
evt.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == controlID)
{
GUIUtility.hotControl = 0;
evt.Use();
return true;
}
break;
case EventType.MouseMove:
if (HandleUtility.nearestControl == controlID)
HandleUtility.Repaint();
break;
}
return false;
}
public static void CurveHandleWithKnotInsert(CurveData curve, int controlID, bool activeSpline)
{
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(controlID);
CurveHandleCap(curve, controlID, eventType, false, activeSpline);
switch (eventType)
{
case EventType.Repaint:
if (HandleUtility.nearestControl == controlID)
{
SplineHandleUtility.GetNearestPointOnCurve(curve, out Vector3 position, out float t);
if(t > 0f && t < 1f)
{
var mouseRect = new Rect(evt.mousePosition - new Vector2(500, 500), new Vector2(1000, 1000));
EditorGUIUtility.AddCursorRect(mouseRect, MouseCursor.ArrowPlus);
var previewKnotRotation = quaternion.identity;
if (curve.a.spline is BezierEditableSpline)
{
var bezierCurve = BezierCurve.FromTangent(curve.a.position, curve.a.GetTangent((int)BezierTangent.Out).direction,
curve.b.position, curve.b.GetTangent((int)BezierTangent.In).direction);
var up = CurveUtility.EvaluateUpVector(bezierCurve, t, math.rotate(curve.a.rotation, math.up()), math.rotate(curve.b.rotation, math.up()));
var tangentOut = CurveUtility.EvaluateTangent(bezierCurve, t);
previewKnotRotation = quaternion.LookRotationSafe(math.normalize(tangentOut), up);
}
DrawKnotHandle(position, previewKnotRotation, false, controlID, false, activeSpline);
}
}
break;
case EventType.MouseDown:
if (HandleUtility.nearestControl == controlID)
{
if (evt.button != 0)
break;
SplineHandleUtility.GetNearestPointOnCurve(curve, out Vector3 position, out float t);
//Do not place a new knot on an existing one to prevent creation of singularity points with bad tangents
if(t > 0f && t < 1f)
{
EditableKnot knot = EditableSplineUtility.InsertKnotOnCurve(curve, position, t);
if(!(evt.control || evt.shift))
SplineSelection.Clear();
SplineSelection.Add(knot);
}
evt.Use();
}
break;
case EventType.MouseMove:
if(HandleUtility.nearestControl == controlID)
HandleUtility.Repaint();
break;
}
}
internal static void DrawKnotHandle(EditableKnot knot, List<int> tangentControlIDs = null, bool activeSpline = true)
{
DrawKnotHandle(knot, -1, tangentControlIDs, activeSpline);
}
internal static void DrawKnotHandle(EditableKnot knot, int controlId = -1, List<int> tangentControlIDs = null, bool activeSpline = true)
{
var mirroredTangentSelected = false;
var mirroredTangentHovered = false;
if (tangentControlIDs != null && knot is BezierEditableKnot bezierKnot &&
(bezierKnot.mode == BezierEditableKnot.Mode.Mirrored || bezierKnot.mode == BezierEditableKnot.Mode.Continuous))
{
for (int i = 0; i < knot.tangentCount; i++)
{
var tangent = knot.GetTangent(i);
if (SplineSelection.Contains(tangent))
{
mirroredTangentSelected = true;
break;
}
}
if (!mirroredTangentSelected)
{
foreach (var tangentID in tangentControlIDs)
{
if (HandleUtility.nearestControl == tangentID)
mirroredTangentHovered = true;
}
}
}
DrawKnotHandle(knot.position, knot.rotation, SplineSelection.Contains(knot), controlId, false, activeSpline, mirroredTangentSelected, mirroredTangentHovered);
}
internal static void DrawKnotHandle(Vector3 knotPosition, Quaternion knotRotation, bool selected, int controlId,
bool preview = false, bool activeSpline = true, bool mirroredTangentSelected = false, bool mirroredTangentHovered = false)
{
if(Event.current.GetTypeForControl(controlId) != EventType.Repaint)
return;
var knotColor = s_KnotColor.value;
if(preview)
knotColor = Color.Lerp(Color.gray, Color.white, 0.5f);
else if(selected)
knotColor = Handles.selectedColor;
else if(controlId > 0 && GUIUtility.hotControl == 0 && HandleUtility.nearestControl == controlId)
knotColor = Handles.preselectionColor;
if(!activeSpline)
knotColor = Handles.secondaryColor;
var handleSize = HandleUtility.GetHandleSize(knotPosition);
var hovered = HandleUtility.nearestControl == controlId;
using (new Handles.DrawingScope(knotColor, Matrix4x4.TRS(knotPosition, knotRotation, Vector3.one)))
{
// Knot disc
if (selected || hovered)
{
var radius = selected ? k_KnotDiscRadiusFactorSelected : k_KnotDiscRadiusFactorHover;
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, radius * handleSize);
}
else
Handles.DrawWireDisc(Vector3.zero, Vector3.up, k_KnotDiscRadiusFactorDefault * handleSize, k_HandleWidthHover * k_AliasedLineSizeMultiplier);
}
var rotationDiscColor = knotColor;
if (!selected && mirroredTangentSelected)
rotationDiscColor = Handles.selectedColor;
using (new Handles.DrawingScope(rotationDiscColor, Matrix4x4.TRS(knotPosition, knotRotation, Vector3.one)))
{
// Knot rotation indicators
var rotationDiscWidth = k_KnotRotDiscWidthDefault;
if (selected || mirroredTangentSelected)
rotationDiscWidth = k_KnotRotDiscWidthSelected;
else if (hovered || mirroredTangentHovered)
rotationDiscWidth = k_KnotRotDiscWidthHover;
DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * handleSize, rotationDiscWidth);
s_SegmentBuffer[0] = Vector3.zero;
s_SegmentBuffer[1] = Vector3.up * 2f * k_SizeFactor * handleSize;
Handles.DrawAAPolyLine(k_HandleWidthDefault, s_SegmentBuffer);
}
}
internal static void DrawPreviewKnot(EditableKnot knot)
{
DrawKnotHandle(knot.position, knot.rotation, false, -1, true);
}
internal static void DrawTangentHandle(EditableTangent tangent, int controlId = -1, bool activeHandle = true)
{
if(Event.current.type != EventType.Repaint)
return;
var knotPos = tangent.owner.position;
var tangentPos = tangent.position;
var tangentHandleSize = HandleUtility.GetHandleSize(tangentPos);
var tangentColor = s_KnotColor.value;
var selected = SplineSelection.Contains(tangent);
var hovered = HandleUtility.nearestControl == controlId;
if (selected)
tangentColor = Handles.selectedColor;
else if (hovered)
tangentColor = Handles.preselectionColor;
if(!activeHandle)
tangentColor = Handles.secondaryColor;
var tangentArmColor = tangentColor == s_KnotColor ? s_TangentColor.value : tangentColor;
var oppositeSelected = IsOppositeTangentSelected(tangent);
if (tangentArmColor == s_TangentColor && oppositeSelected)
tangentArmColor = Handles.selectedColor;
var oppositeHovered = IsOppositeTangentHovered(tangent);
var mirrored = (tangent.owner is BezierEditableKnot bezierKnot) &&
bezierKnot.mode == BezierEditableKnot.Mode.Mirrored;
using (new ColorScope(tangentArmColor))
{
var width = k_TangentLineWidthDefault;
if (selected || (mirrored && oppositeSelected))
width = k_TangentLineWidthSelected;
else if (hovered || (mirrored && oppositeHovered))
width = k_TangentLineWidthHover;
var tex = width > k_TangentLineWidthDefault ? s_ThickTangentLineAATex : null;
var startPos = knotPos;
var toTangent = tangentPos - knotPos;
var toTangentNorm = math.normalize(toTangent);
var length = math.length(toTangent);
var knotHandleSize = HandleUtility.GetHandleSize(startPos);
var knotHandleOffset = knotHandleSize * k_TangentStartOffsetFromKnot;
var tangentHandleOffset = tangentHandleSize * k_tangentEndOffsetFromHandle;
// Reduce the length slightly, so that there's some space between tangent line endings and handles.
length = Mathf.Max(0f, length - knotHandleOffset - tangentHandleOffset);
startPos += toTangentNorm * knotHandleOffset;
SplineHandleUtility.DrawLineWithWidth(startPos + toTangentNorm * length, startPos, width, tex);
}
var rotation = TransformOperation.CalculateElementSpaceHandleRotation(math.length(tangent.localPosition) >0 ? tangent : tangent.owner);
using (new Handles.DrawingScope(tangentColor, Matrix4x4.TRS(tangent.position, rotation, Vector3.one)))
{
if (selected || hovered)
{
var radius = (selected ? k_KnotDiscRadiusFactorSelected : k_KnotDiscRadiusFactorHover) * tangentHandleSize;
// As Handles.DrawSolidDisc has no thickness parameter, we're drawing a wire disc here so that the solid disc has thickness when viewed from a shallow angle.
Handles.DrawWireDisc(Vector3.zero, Vector3.up, radius * 0.7f, k_HandleWidthHover);
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, radius);
}
else
Handles.DrawWireDisc(Vector3.zero, Vector3.up, k_KnotDiscRadiusFactorDefault * tangentHandleSize, k_HandleWidthHover * k_AliasedLineSizeMultiplier);
}
}
static void DrawAAWireDisc(Vector3 position, Vector3 normal, float radius, float thickness)
{
// Right vector calculation here is identical to Handles.DrawWireDisc
Vector3 right = Vector3.Cross(normal, Vector3.up);
if ((double)right.sqrMagnitude < 1.0 / 1000.0)
right = Vector3.Cross(normal, Vector3.right);
var angleStep = 360f / (s_AAWireDiscBuffer.Length - 1);
for (int i = 0; i < s_AAWireDiscBuffer.Length - 1; i++)
{
s_AAWireDiscBuffer[i] = position + right * radius;
right = Quaternion.AngleAxis(angleStep, normal) * right;
}
s_AAWireDiscBuffer[s_AAWireDiscBuffer.Length - 1] = s_AAWireDiscBuffer[0];
var tex = thickness > 2f ? s_ThickTangentLineAATex : null;
Handles.DrawAAPolyLine(tex, thickness, s_AAWireDiscBuffer);
}
static bool IsOppositeTangentSelected(EditableTangent tangent)
{
return tangent.owner is BezierEditableKnot knot
&& knot.mode != BezierEditableKnot.Mode.Broken
&& knot.TryGetOppositeTangent(tangent, out EditableTangent oppositeTangent)
&& SplineSelection.Contains(oppositeTangent);
}
static bool IsOppositeTangentHovered(EditableTangent tangent)
{
return tangent.owner is BezierEditableKnot knot
&& knot.TryGetOppositeTangent(tangent, out EditableTangent oppositeTangent)
&& (s_LastHoveredTangent == oppositeTangent);
}
internal static void DrawCurve(CurveData curve)
{
Event evt = Event.current;
if (evt.type == EventType.Repaint)
CurveHandleCap(curve, -1, EventType.Repaint);
}
internal static void CurveHandleCap(CurveData curve, int controlID, EventType eventType, bool previewCurve = false, bool activeSpline = true)
{
switch (eventType)
{
case EventType.Layout:
{
SplineHandleUtility.GetCurveSegments(curve, s_CurveSegmentsBuffer);
float dist = float.MaxValue;
for (var i = 0; i < s_CurveSegmentsBuffer.Length - 1; ++i)
{
var a = s_CurveSegmentsBuffer[i];
var b = s_CurveSegmentsBuffer[i + 1];
dist = Mathf.Min(HandleUtility.DistanceToLine(a, b), dist);
}
if (!Tools.viewToolActive)
HandleUtility.AddControl(controlID, Mathf.Max(0, dist - k_PickingDistance));
break;
}
case EventType.Repaint:
{
SplineHandleUtility.GetCurveSegments(curve, s_CurveSegmentsBuffer);
//We attenuate the spline display if a spline can be controlled (id != -1) and
//if it's not the current active spline
var attenuate = controlID != -1 && !activeSpline;
var prevColor = Handles.color;
var color = s_LineNormalFrontColor.value;
if (attenuate)
color = Handles.secondaryColor;
if (previewCurve)
color.a *= k_PreviewCurveOpacity;
Handles.color = color;
using (new ZTestScope(CompareFunction.Less))
{
Handles.DrawAAPolyLine(k_CurveLineWidth, s_CurveSegmentsBuffer);
}
color = s_LineNormalBehindColor.value;
if (attenuate)
color = Handles.secondaryColor;
if (previewCurve)
color.a *= k_PreviewCurveOpacity;
Handles.color = color;
using (new ZTestScope(CompareFunction.Greater))
{
Handles.DrawAAPolyLine(k_CurveLineWidth, s_CurveSegmentsBuffer);
}
Handles.color = prevColor;
break;
}
}
}
}
}

View file

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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 231d226261711dc47be16b8c01e46909
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,72 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
using Unity.Mathematics;
namespace UnityEditor.Splines
{
struct BezierBuilder
{
readonly List<BezierKnot> m_ResultKnots;
readonly bool m_Closed;
int knotCount => m_ResultKnots.Count;
public int segmentCount => m_Closed ? m_ResultKnots.Count : m_ResultKnots.Count - 1;
public BezierBuilder(List<BezierKnot> result, bool closed, int targetKnotCount)
{
m_ResultKnots = result;
for (int i = 0; i < targetKnotCount; ++i)
{
var knot = new BezierKnot();
knot.Rotation = quaternion.identity;
m_ResultKnots.Add(knot);
}
m_Closed = closed;
}
public void SetKnot(int index, float3 position, float3 tangentIn, float3 tangentOut, quaternion rotation)
{
var current = m_ResultKnots[index];
current.Position = position;
current.TangentIn = tangentIn;
current.TangentOut = tangentOut;
current.Rotation = rotation;
m_ResultKnots[index] = current;
}
void GetSegmentEndIndex(int index, out int endIndex)
{
endIndex = m_Closed ? (index + 1) % knotCount : index + 1;
}
public void SetSegment(int index, float3 posA, float3 tangentOutA, quaternion rotationA, float3 posB, float3 tangentInB, quaternion rotationB)
{
GetSegmentEndIndex(index, out int nextIndex);
var current = m_ResultKnots[index];
current.Position = posA;
current.Rotation = rotationA;
current.TangentOut = tangentOutA;
var next = m_ResultKnots[nextIndex];
next.Position = posB;
next.Rotation = rotationB;
next.TangentIn = tangentInB;
if (!m_Closed)
{
if (index == 0)
current.TangentIn = -current.TangentOut;
else if (nextIndex == knotCount - 1)
next.TangentOut = -next.TangentIn;
}
m_ResultKnots[index] = current;
m_ResultKnots[nextIndex] = next;
}
}
}

View file

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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 411641953fc0a2148a0507f5ee6df182
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

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

View file

@ -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) {}
}
}

View file

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

View file

@ -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);
}
}

View file

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

View file

@ -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);
}
}
}
}

View file

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

View file

@ -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;
}
}
}

View file

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

View file

@ -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;
}
}
}

View file

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

View file

@ -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;
}
}
}

View file

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

View file

@ -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();
}
}
}

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f0575e8c5ac74a399a62fcb72fe081c4
timeCreated: 1634919075

View file

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
sealed class ButtonStripField : VisualElement
{
static readonly StyleSheet s_StyleSheet;
static ButtonStripField()
{
s_StyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Packages/com.unity.splines/Editor/Stylesheets/ButtonStripField.uss");
}
const string k_ButtonStripClass = "button-strip";
const string k_ButtonClass = "button-strip-button";
const string k_ButtonIconClass = "button-strip-button__icon";
const string k_LeftButtonClass = k_ButtonClass + "--left";
const string k_MiddleButtonClass = k_ButtonClass + "--middle";
const string k_RightButtonClass = k_ButtonClass + "--right";
const string k_AloneButtonClass = k_ButtonClass + "--alone";
const string k_CheckedButtonClass = k_ButtonClass + "--checked";
GUIContent[] m_Choices = new GUIContent[0];
readonly VisualElement m_ButtonStrip;
public GUIContent[] choices
{
get => m_Choices;
set
{
m_Choices = value ?? new GUIContent[0];
RebuildButtonStrip();
}
}
int m_Value;
public int value
{
get => m_Value;
set
{
m_Value = value;
UpdateButtonsState(m_Value);
OnValueChanged?.Invoke(m_Value);
}
}
public event Action<int> OnValueChanged;
public ButtonStripField()
{
styleSheets.Add(s_StyleSheet);
m_ButtonStrip = this;
m_ButtonStrip.AddToClassList(k_ButtonStripClass);
}
Button CreateButton(GUIContent content)
{
var button = new Button();
button.displayTooltipWhenElided = false;
button.tooltip = L10n.Tr(content.tooltip);
var icon = new VisualElement { name = content.text };
icon.AddToClassList(k_ButtonIconClass);
button.AddToClassList(k_ButtonClass);
button.Add(icon);
return button;
}
//public override void SetValueWithoutNotify(int newValue)
public void SetValueWithoutNotify(int newValue)
{
m_Value = math.clamp(newValue, 0, choices.Length - 1);
UpdateButtonsState(m_Value);
}
void UpdateButtonsState(int value)
{
List<Button> buttons = m_ButtonStrip.Query<Button>().ToList();
for (int i = 0; i < buttons.Count; ++i)
{
buttons[i].EnableInClassList(k_CheckedButtonClass, value == i);
}
}
void RebuildButtonStrip()
{
m_ButtonStrip.Clear();
for (int i = 0, count = choices.Length; i < count; ++i)
{
var button = CreateButton(choices[i]);
var targetValue = i;
button.clicked += () => { value = targetValue; };
if (choices.Length == 1)
button.AddToClassList(k_AloneButtonClass);
else if (i == 0)
button.AddToClassList(k_LeftButtonClass);
else if (i == count - 1)
button.AddToClassList(k_RightButtonClass);
else
button.AddToClassList(k_MiddleButtonClass);
m_ButtonStrip.Add(button);
}
UpdateButtonsState(value);
}
}
}

View file

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

View file

@ -0,0 +1,26 @@
using UnityEditor.EditorTools;
using UnityEngine;
using UnityEngine.Splines;
using UObject = UnityEngine.Object;
namespace UnityEditor.Splines
{
static class EditorSplineGizmos
{
[DrawGizmo(GizmoType.Active | GizmoType.NonSelected | GizmoType.Selected | GizmoType.Pickable)]
static void DrawUnselectedSplineGizmos(ISplineProvider provider, GizmoType gizmoType)
{
//Skip if tool engaged is a spline tool
if (typeof(SplineTool).IsAssignableFrom(ToolManager.activeToolType) &&
(provider is UObject objectProvider) && EditableSplineManager.TryGetTargetData(objectProvider, out _))
return;
var prev = Gizmos.color;
Gizmos.color = (gizmoType & (GizmoType.Selected | GizmoType.Active)) > 0
? Handles.selectedColor
: SplineGizmoUtility.s_GizmosLineColor.value;
SplineGizmoUtility.DrawGizmos(provider);
Gizmos.color = prev;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a0357b2fb04a410d8dc62740f4fe4cbb
timeCreated: 1618421082

View file

@ -0,0 +1,94 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.Toolbars;
namespace UnityEditor.Splines
{
[EditorToolbarElement("Spline Tool Settings/Handle Rotation")]
class HandleRotationDropdown : EditorToolbarDropdown
{
const string k_ParentRotationIconPath = "Packages/com.unity.splines/Editor/Resources/Icons/ToolHandleParent.png";
const string k_ElementRotationIconPath = "Packages/com.unity.splines/Editor/Resources/Icons/ToolHandleElement.png";
readonly List<GUIContent> m_OptionContents = new List<GUIContent>();
public HandleRotationDropdown()
{
name = "Handle Rotation";
var content = EditorGUIUtility.TrTextContent("Local",
"Toggle Tool Handle Rotation\n\nTool handles are in the active object's rotation.",
"ToolHandleLocal");
m_OptionContents.Add(content);
content = EditorGUIUtility.TrTextContent("Global",
"Toggle Tool Handle Rotation\n\nTool handles are in global rotation.",
"ToolHandleGlobal");
m_OptionContents.Add(content);
content = EditorGUIUtility.TrTextContent("Parent",
"Toggle Tool Handle Rotation\n\nTool handles are in active element's parent's rotation.",
k_ParentRotationIconPath);
m_OptionContents.Add(content);
content = EditorGUIUtility.TrTextContent("Element",
"Toggle Tool Handle Rotation\n\nTool handles are in active element's rotation.",
k_ElementRotationIconPath);
m_OptionContents.Add(content);
RegisterCallback<AttachToPanelEvent>(AttachedToPanel);
RegisterCallback<DetachFromPanelEvent>(DetachedFromPanel);
clicked += OpenContextMenu;
RefreshElementContent();
}
void OpenContextMenu()
{
var menu = new GenericMenu();
menu.AddItem(m_OptionContents[(int)HandleOrientation.Global], SplineTool.handleOrientation == HandleOrientation.Global,
() => SetHandleOrientationIfNeeded(HandleOrientation.Global));
menu.AddItem(m_OptionContents[(int)HandleOrientation.Local], SplineTool.handleOrientation == HandleOrientation.Local,
() => SetHandleOrientationIfNeeded(HandleOrientation.Local));
menu.AddItem(m_OptionContents[(int)HandleOrientation.Parent], SplineTool.handleOrientation == HandleOrientation.Parent,
() => SetHandleOrientationIfNeeded(HandleOrientation.Parent));
menu.AddItem(m_OptionContents[(int)HandleOrientation.Element], SplineTool.handleOrientation == HandleOrientation.Element,
() => SetHandleOrientationIfNeeded(HandleOrientation.Element));
menu.DropDown(worldBound);
}
void SetHandleOrientationIfNeeded(HandleOrientation handleOrientation)
{
if (SplineTool.handleOrientation != handleOrientation)
{
SplineTool.handleOrientation = handleOrientation;
RefreshElementContent();
}
}
void RefreshElementContent()
{
var content = m_OptionContents[(int)SplineTool.handleOrientation];
text = content.text;
tooltip = content.tooltip;
icon = content.image as Texture2D;
}
void AttachedToPanel(AttachToPanelEvent evt)
{
SplineTool.handleOrientationChanged += RefreshElementContent;
}
void DetachedFromPanel(DetachFromPanelEvent evt)
{
SplineTool.handleOrientationChanged -= RefreshElementContent;
}
}
}

View file

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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eaa3cd6309cba3244b2a227f3e8e7eb3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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

View file

@ -0,0 +1,60 @@
using System;
using UnityEngine;
using UnityEditor;
class SplineComponentEditor : Editor
{
static GUIStyle s_HorizontalLine;
static GUIStyle s_FoldoutStyle;
protected virtual void OnEnable()
{
if (s_HorizontalLine == null)
{
s_HorizontalLine = new GUIStyle();
s_HorizontalLine.normal.background = EditorGUIUtility.whiteTexture;
s_HorizontalLine.margin = new RectOffset(0, 0, 3, 3);
s_HorizontalLine.fixedHeight = 1;
}
}
protected void HorizontalLine(Color color)
{
var c = GUI.color;
GUI.color = color;
GUILayout.Box( GUIContent.none, s_HorizontalLine );
GUI.color = c;
}
protected bool Foldout(bool foldout, GUIContent content)
{
return Foldout(foldout, content, false);
}
public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick)
{
if (s_FoldoutStyle == null)
{
s_FoldoutStyle = new GUIStyle(EditorStyles.foldout);
s_FoldoutStyle.fontStyle = FontStyle.Bold;
}
return EditorGUILayout.Foldout(foldout, content, toggleOnLabelClick, s_FoldoutStyle);
}
internal struct LabelWidthScope : IDisposable
{
float previousWidth;
public LabelWidthScope(float width)
{
previousWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = width;
}
public void Dispose()
{
EditorGUIUtility.labelWidth = previousWidth;
}
}
}

View file

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

View file

@ -0,0 +1,100 @@
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines.Editor.GUI
{
class SplineDataConversionWindow : EditorWindow
{
readonly static string k_SplineDataConversionMessage = L10n.Tr("Select a reference spline to convert your SplineData with no data loss. Otherwise data won't be converted.");
SerializedProperty m_SplineDataProperty;
SerializedProperty m_SplineDataUnitProperty;
FieldInfo m_FieldInfo;
SplineContainer m_TargetSpline;
int m_NewValue;
public static void DoConfirmWindow(SerializedProperty property, SerializedProperty unitProperty, FieldInfo fieldInfo, Component targetComponent, int newValue)
{
// Get existing open window or if none, make a new one:
var window = (SplineDataConversionWindow)GetWindow(typeof(SplineDataConversionWindow));
window.m_SplineDataProperty = property;
window.m_SplineDataUnitProperty = unitProperty;
window.m_FieldInfo = fieldInfo;
window.m_TargetSpline = FindPlausibleSplineContainer(targetComponent);
window.m_NewValue = newValue;
window.minSize = new Vector2(400, 100);
window.maxSize = new Vector2(400, 100);
window.Show();
}
void OnGUI()
{
GUILayout.Label(L10n.Tr("Spline Data Conversion"), EditorStyles.boldLabel);
if(m_TargetSpline == null)
EditorGUILayout.HelpBox(k_SplineDataConversionMessage,MessageType.Warning);
else
EditorGUILayout.HelpBox(L10n.Tr($"The spline {m_TargetSpline} will be used for data conversion."),MessageType.Info);
m_TargetSpline = (SplineContainer)EditorGUILayout.ObjectField(
"Reference Spline",
(Object)m_TargetSpline,
typeof(SplineContainer),
true);
EditorGUILayout.BeginHorizontal();
if(GUILayout.Button(new GUIContent(L10n.Tr("Convert"), L10n.Tr("Convert data indexes to the new Unit."))))
{
if(m_TargetSpline != null)
ApplyConversion();
else
ApplyWithNoConversion();
Close();
}
if(GUILayout.Button(new GUIContent(L10n.Tr("Don't Convert"), L10n.Tr("Do not convert data indexes."))))
{
ApplyWithNoConversion();
Close();
}
if(GUILayout.Button(L10n.Tr("Cancel")))
Close();
EditorGUILayout.EndHorizontal();
}
static SplineContainer FindPlausibleSplineContainer(Component targetComponent)
{
SplineContainer container = null;
var fieldInfos = targetComponent.GetType().GetFields(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
var providerFieldInfo = fieldInfos.FirstOrDefault(field => field.FieldType == typeof(SplineContainer));
if(providerFieldInfo != null && providerFieldInfo.FieldType == typeof(SplineContainer))
container = (SplineContainer)providerFieldInfo.GetValue(targetComponent);
if(container == null)
container = targetComponent.gameObject.GetComponent<SplineContainer>();
return container;
}
void ApplyWithNoConversion()
{
m_SplineDataUnitProperty.intValue = m_NewValue;
m_SplineDataUnitProperty.serializedObject.ApplyModifiedProperties();
}
void ApplyConversion()
{
var targetObject = m_FieldInfo.GetValue(m_SplineDataProperty.serializedObject.targetObject);
var convertMethod = targetObject.GetType().GetMethod("ConvertPathUnit", BindingFlags.Instance | BindingFlags.Public)?
.MakeGenericMethod(typeof(NativeSpline));
convertMethod?.Invoke(targetObject, new object[]
{
new NativeSpline(m_TargetSpline.Spline, m_TargetSpline.transform.localToWorldMatrix),
(PathIndexUnit)m_NewValue
});
m_SplineDataProperty.serializedObject.ApplyModifiedProperties();
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 97db6026b4154fbf97b957dd2137810d
timeCreated: 1634919075

View file

@ -0,0 +1,286 @@
using System;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
/// <summary>
/// Provides methods for drawing <see cref="SplineData"/> manipulation handles.
/// </summary>
public static class SplineDataHandles
{
const float k_HandleSize = 0.15f;
const int k_PickRes = 2;
static int[] s_DataPointsIDs;
static int s_NewDataPointIndex = -1;
static bool s_AddingDataPoint = false;
static bool m_ShowAddHandle;
static float3 m_Position;
static float m_T;
/// <summary>
/// Draw default manipulation handles that enables adding, removing and moving
/// DataPoints of the targeted SplineData along a Spline. Left click on an empty location
/// on the spline adds a new DataPoint in the SplineData. Left click on an existing DataPoint
/// allows to move this point along the Spline while a right click on it allows to delete that DataPoint.
/// </summary>
/// <param name="spline">The Spline to use to interpret the SplineData.</param>
/// <param name="splineData">The SplineData for which the handles are drawn.</param>
/// <typeparam name="TSpline">The Spline type.</typeparam>
/// <typeparam name="TData">The type of data this data point stores.</typeparam>
public static void DataPointHandles<TSpline, TData>(
this TSpline spline,
SplineData<TData> splineData)
where TSpline : ISpline
{
var evt = Event.current;
if(evt.type == EventType.MouseMove)
{
//Compute distance to spline and closest point
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
var distance = SplineUtility.GetNearestPoint(spline, ray, out m_Position, out m_T);
m_ShowAddHandle = distance < HandleUtility.GetHandleSize(m_Position);
}
//Id has to be consistent no matter the distance test
var id = GUIUtility.GetControlID(FocusType.Passive);
//Only activating the tooling when close enough from the spline
if(m_ShowAddHandle)
DataPointAddHandle(id, spline, splineData, m_Position, m_T);
//Remove DataPoint functionality
TryRemoveDataPoint(splineData);
//Draw Default manipulation handles
DataPointMoveHandles(spline, splineData);
}
static void TryRemoveDataPoint<TData>(SplineData<TData> splineData)
{
var evt = Event.current;
//Remove data point only when not adding one and when using right click button
if(!s_AddingDataPoint && GUIUtility.hotControl == 0
&& evt.type == EventType.MouseDown && evt.button == 1
&& s_DataPointsIDs.Contains(HandleUtility.nearestControl))
{
var dataPointIndex = splineData.Indexes.ElementAt(Array.IndexOf(s_DataPointsIDs, HandleUtility.nearestControl));
splineData.RemoveDataPoint(dataPointIndex);
evt.Use();
}
}
static void DataPointAddHandle<TSpline, TData>(
int controlID,
TSpline spline,
SplineData<TData> splineData,
float3 pos,
float t)
where TSpline : ISpline
{
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(controlID);
switch (eventType)
{
case EventType.Layout:
{
if(!Tools.viewToolActive)
HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(pos, 0.1f));
break;
}
case EventType.Repaint:
if(HandleUtility.nearestControl == controlID && GUIUtility.hotControl == 0 || s_AddingDataPoint)
{
var upDir = spline.EvaluateUpVector(t);
Handles.CircleHandleCap(controlID, pos, Quaternion.LookRotation(upDir), 0.15f * HandleUtility.GetHandleSize(pos), EventType.Repaint);
}
break;
case EventType.MouseDown:
if (evt.button == 0
&& HandleUtility.nearestControl == controlID
&& GUIUtility.hotControl == 0)
{
s_AddingDataPoint = true;
var index = SplineUtility.ConvertIndexUnit(
spline, t,
splineData.PathIndexUnit);
s_NewDataPointIndex = splineData.AddDataPointWithDefaultValue(index);
evt.Use();
}
break;
case EventType.MouseDrag:
if (evt.button == 0 && s_AddingDataPoint)
{
GUIUtility.hotControl = 0;
var index = SplineUtility.ConvertIndexUnit(
spline, t,
splineData.PathIndexUnit);
s_NewDataPointIndex = splineData.MoveDataPoint(s_NewDataPointIndex, index);
evt.Use();
}
break;
case EventType.MouseUp:
if (evt.button == 0 && s_AddingDataPoint)
{
s_AddingDataPoint = false;
s_NewDataPointIndex = -1;
GUIUtility.hotControl = 0;
evt.Use();
}
break;
case EventType.MouseMove:
HandleUtility.Repaint();
break;
}
}
static void DataPointMoveHandles<TSpline, TData>(TSpline spline, SplineData<TData> splineData)
where TSpline : ISpline
{
if(s_DataPointsIDs == null || s_DataPointsIDs.Length != splineData.Count)
s_DataPointsIDs = new int[splineData.Count];
//Cache all data point IDs
for(int dataIndex = 0; dataIndex < splineData.Count; dataIndex++)
s_DataPointsIDs[dataIndex] = GUIUtility.GetControlID(FocusType.Passive);
//Draw all data points handles on the spline
for(int dataIndex = 0; dataIndex < splineData.Count; dataIndex++)
{
var id = GUIUtility.GetControlID(FocusType.Passive);
var index = splineData.Indexes.ElementAt(dataIndex);
SplineDataHandle(
s_DataPointsIDs[dataIndex],
spline,
splineData,
index,
k_HandleSize,
out float newIndex);
if(GUIUtility.hotControl == s_DataPointsIDs[dataIndex])
{
var newDataIndex = splineData.MoveDataPoint(dataIndex, newIndex);
//If the current DataPoint is moved across another DataPoint, then update the hotControl ID
if(newDataIndex - index != 0)
GUIUtility.hotControl = s_DataPointsIDs[newDataIndex];
}
}
}
static void SplineDataHandle<TSpline, TData>(
int controlID,
TSpline spline,
SplineData<TData> splineData,
float dataPointIndex,
float size,
out float newTime) where TSpline : ISpline
{
newTime = dataPointIndex;
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(controlID);
var normalizedT = SplineUtility.GetNormalizedInterpolation(spline, dataPointIndex, splineData.PathIndexUnit);
var dataPosition = SplineUtility.EvaluatePosition(spline, normalizedT);
switch (eventType)
{
case EventType.Layout:
var dist = HandleUtility.DistanceToCircle(dataPosition, size * HandleUtility.GetHandleSize(dataPosition));
HandleUtility.AddControl(controlID, dist);
break;
case EventType.Repaint:
DrawSplineDataHandle(controlID, dataPosition, size);
break;
case EventType.MouseDown:
if (evt.button == 0
&& HandleUtility.nearestControl == controlID
&& GUIUtility.hotControl == 0)
{
GUIUtility.hotControl = controlID;
newTime = GetClosestSplineDataT(spline, splineData);
evt.Use();
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == controlID)
{
newTime = GetClosestSplineDataT(spline, splineData);
evt.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == controlID)
{
if(evt.button == 0)
{
GUIUtility.hotControl = 0;
newTime = GetClosestSplineDataT(spline, splineData);
}
evt.Use();
}
break;
case EventType.MouseMove:
HandleUtility.Repaint();
break;
}
}
static void DrawSplineDataHandle(int controlID, Vector3 position, float size)
{
var handleColor = Handles.color;
if(controlID == GUIUtility.hotControl)
handleColor = Handles.selectedColor;
else if(GUIUtility.hotControl == 0 && controlID == HandleUtility.nearestControl)
handleColor = Handles.preselectionColor;
// to avoid affecting the sphere dimensions with the handles matrix, we'll just use the position and reset
// the matrix to identity when drawing.
position = Handles.matrix * position;
using(new Handles.DrawingScope(handleColor, Matrix4x4.identity))
{
Handles.SphereHandleCap(
controlID,
position,
Quaternion.identity,
size * HandleUtility.GetHandleSize(position),
EventType.Repaint
);
}
}
// Spline must be in world space
static float GetClosestSplineDataT<TSpline,TData>(TSpline spline, SplineData<TData> splineData) where TSpline : ISpline
{
var evt = Event.current;
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
SplineUtility.GetNearestPoint(spline,
ray,
out float3 _,
out float t,
k_PickRes);
return SplineUtility.ConvertIndexUnit(spline, t, splineData.PathIndexUnit);
}
}
}

View file

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

View file

@ -0,0 +1,97 @@
using System;
using UnityEditor.Splines.Editor.GUI;
using UnityEngine;
using UnityEngine.Internal;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
[ExcludeFromDocs]
[CustomPropertyDrawer(typeof(SplineData<>))]
public class SplineDataPropertyDrawer : PropertyDrawer
{
readonly static string k_MultiSplineEditMessage = L10n.Tr("Multi-selection is not supported for SplineData");
readonly static string k_DataUnitTooltip = L10n.Tr("The unit Data Points are using to be associated to the spline. 'Spline Distance' is " +
"using the distance in Unity Units from the spline origin, 'Normalized Distance' is using a normalized value of the spline " +
"length between [0,1] and 'Knot Index' is using Spline Knot indeces.");
readonly static GUIContent[] k_PathUnitIndexLabels = new[]
{
new GUIContent(L10n.Tr("Spline Distance")),
new GUIContent(L10n.Tr("Normalized Distance")),
new GUIContent(L10n.Tr("Knot Index"))
};
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float height = EditorGUIUtility.singleLineHeight;
if(!property.isExpanded || property.serializedObject.isEditingMultipleObjects)
return height;
//Adding space for the object field
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
height += EditorGUI.GetPropertyHeight(property.FindPropertyRelative("m_IndexUnit")) + EditorGUIUtility.standardVerticalSpacing;
var datapointsProperty = property.FindPropertyRelative("m_DataPoints");
height += EditorGUIUtility.singleLineHeight;
if(datapointsProperty.isExpanded)
{
height += 2 * EditorGUIUtility.singleLineHeight;
var arraySize = datapointsProperty.arraySize;
if(arraySize == 0)
{
height += EditorGUIUtility.singleLineHeight;
}
else
{
for(int dataPointIndex = 0; dataPointIndex < arraySize; dataPointIndex++)
{
height += datapointsProperty.GetArrayElementAtIndex(dataPointIndex).isExpanded
? 3 * EditorGUIUtility.singleLineHeight + 2 * EditorGUIUtility.standardVerticalSpacing
: EditorGUIUtility.singleLineHeight;
}
}
}
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
if(property.serializedObject.isEditingMultipleObjects)
{
EditorGUI.LabelField(position,L10n.Tr(k_MultiSplineEditMessage), EditorStyles.helpBox);
return;
}
property.isExpanded = EditorGUI.Foldout(SplineUIManager.ReserveSpace(EditorGUIUtility.singleLineHeight, ref position), property.isExpanded, label);
if (property.isExpanded)
{
EditorGUI.indentLevel++;
SplineUIManager.ReserveSpace(EditorGUIUtility.standardVerticalSpacing, ref position);
var indexProperty = property.FindPropertyRelative("m_IndexUnit");
var dataPointsProperty = property.FindPropertyRelative("m_DataPoints");
var pathUnit = (PathIndexUnit)indexProperty.intValue;
EditorGUI.BeginChangeCheck();
var newPathUnit = EditorGUI.Popup(SplineUIManager.ReserveSpace(EditorGUI.GetPropertyHeight(indexProperty), ref position),
new GUIContent("Data Index Unit",L10n.Tr(k_DataUnitTooltip)), (int)pathUnit, k_PathUnitIndexLabels);
if(EditorGUI.EndChangeCheck())
{
if(dataPointsProperty.arraySize == 0)
indexProperty.intValue = newPathUnit;
else
SplineDataConversionWindow.DoConfirmWindow(property, indexProperty, fieldInfo, property.serializedObject.targetObject as Component, newPathUnit);
}
SplineUIManager.ReserveSpace(EditorGUIUtility.standardVerticalSpacing, ref position);
dataPointsProperty.isExpanded = EditorGUI.Foldout(SplineUIManager.ReserveSpace(EditorGUIUtility.singleLineHeight, ref position), dataPointsProperty.isExpanded, new GUIContent("Data Points"));
if(dataPointsProperty.isExpanded)
SplineDataUIManager.instance.GetDataPointsReorderableList(property, dataPointsProperty, fieldInfo, pathUnit).DoList(position);
}
EditorGUI.EndProperty();
}
}
}

View file

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

View file

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
internal class SplineDataUIManager:ScriptableSingleton<SplineDataUIManager>
{
readonly static string k_DataIndexTooltip = L10n.Tr("The index of the Data Point along the spline and the unit used");
readonly static string k_DataValueTooltip = L10n.Tr("The value of the Data Point.");
static Dictionary<string, ReorderableList> s_ReorderableLists = new Dictionary<string, ReorderableList>();
void OnEnable()
{
Selection.selectionChanged += ClearReorderableLists;
}
void OnDisable()
{
Selection.selectionChanged -= ClearReorderableLists;
}
string GetDisplayName(PathIndexUnit unit)
{
switch(unit)
{
case PathIndexUnit.Distance:
return "Dist";
case PathIndexUnit.Normalized:
return "Path %";
case PathIndexUnit.Knot:
default:
return "Knot";
}
}
void ClearReorderableLists()
{
s_ReorderableLists.Clear();
}
static void SetSplineDataDirty(FieldInfo fieldInfo, SerializedProperty dataPointProperty)
{
var targetObject = fieldInfo.GetValue(dataPointProperty.serializedObject.targetObject);
var dirtyMethod = targetObject.GetType().GetMethod("SetDirty", BindingFlags.Instance | BindingFlags.NonPublic);
dirtyMethod?.Invoke(targetObject, null);
}
public ReorderableList GetDataPointsReorderableList(SerializedProperty property, SerializedProperty dataPointProperty, FieldInfo fieldInfo, PathIndexUnit unit)
{
var key = dataPointProperty.propertyPath + property.serializedObject.targetObject.GetInstanceID();
if(s_ReorderableLists.TryGetValue(key, out var list))
{
try
{
SerializedProperty.EqualContents(list.serializedProperty, dataPointProperty);
return list;
}
catch (NullReferenceException)
{
s_ReorderableLists.Remove(key);
}
}
list = new ReorderableList(dataPointProperty.serializedObject, dataPointProperty, true, false, true, true);
s_ReorderableLists.Add(key, list);
list.elementHeightCallback = (int index) =>
{
return dataPointProperty.arraySize > 0 && dataPointProperty.GetArrayElementAtIndex(index).isExpanded
? 3 * EditorGUIUtility.singleLineHeight + 2 * EditorGUIUtility.standardVerticalSpacing
: EditorGUIUtility.singleLineHeight;
};
list.onChangedCallback = reorderableList =>
{
SetSplineDataDirty(fieldInfo, dataPointProperty);
};
list.drawElementCallback =
(Rect position, int listIndex, bool isActive, bool isFocused) =>
{
var ppte = dataPointProperty.GetArrayElementAtIndex(listIndex);
EditorGUI.indentLevel++;
var expended = EditorGUI.Foldout(SplineUIManager.ReserveSpace(EditorGUIUtility.singleLineHeight, ref position), ppte.isExpanded, new GUIContent($"Data Point [{listIndex}]"), true);
if(expended != ppte.isExpanded)
{
ppte.isExpanded = expended;
if(!isActive)
list.index = listIndex;
list.GrabKeyboardFocus();
}
if(ppte.isExpanded)
{
EditorGUI.indentLevel++;
SplineUIManager.ReserveSpace(EditorGUIUtility.standardVerticalSpacing, ref position);
EditorGUI.BeginChangeCheck();
var indexProperty = ppte.FindPropertyRelative("m_Index");
EditorGUI.DelayedFloatField(SplineUIManager.ReserveSpace(EditorGUIUtility.singleLineHeight, ref position), indexProperty, new GUIContent($"Data Index ({GetDisplayName(unit)})", L10n.Tr(k_DataIndexTooltip)));
if(EditorGUI.EndChangeCheck())
{
if(!isActive)
return;
dataPointProperty.serializedObject.ApplyModifiedProperties();
var newIndex = ppte.FindPropertyRelative("m_Index").floatValue;
var targetObject = fieldInfo.GetValue(dataPointProperty.serializedObject.targetObject);
var sortMethod = targetObject.GetType().GetMethod("ForceSort", BindingFlags.Instance | BindingFlags.NonPublic);
EditorApplication.delayCall += () =>
{
sortMethod?.Invoke(targetObject, null);
dataPointProperty.serializedObject.Update();
for(int i = 0; i < dataPointProperty.arraySize; i++)
{
var index = dataPointProperty.GetArrayElementAtIndex(i).FindPropertyRelative("m_Index").floatValue;
if(index == newIndex)
{
list.index = i;
break;
}
}
};
}
SplineUIManager.ReserveSpace(EditorGUIUtility.standardVerticalSpacing, ref position);
EditorGUI.BeginChangeCheck();
var valueProperty = ppte.FindPropertyRelative("m_Value");
EditorGUI.PropertyField(SplineUIManager.ReserveSpace(EditorGUI.GetPropertyHeight(valueProperty), ref position), valueProperty, new GUIContent("Data Value", L10n.Tr(k_DataValueTooltip)));
if(EditorGUI.EndChangeCheck())
{
SetSplineDataDirty(fieldInfo, dataPointProperty);
if(!isActive)
list.index = listIndex;
}
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
};
return list;
}
}
}

View file

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

View file

@ -0,0 +1,76 @@
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
//This drawer is used to draw the actual type of the spline (editor version) and not the stored spline which is always bezier
[CustomPropertyDrawer(typeof(Spline), true)]
class SplineDrawer : PropertyDrawer
{
const string k_MultiSplineEditMessage = "Multi-selection is not supported for Splines";
const string k_SplineFoldoutTitle = "Advanced";
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float height = EditorGUIUtility.singleLineHeight;
if(!property.isExpanded || property.serializedObject.isEditingMultipleObjects)
return height;
height += EditorGUI.GetPropertyHeight(property.FindPropertyRelative("m_EditModeType"));
var proxy = SplineUIManager.instance.GetProxyFromProperty(property);
var it = proxy.SerializedObject.FindProperty("Spline").Copy();
it.NextVisible(true);
do
{
height += EditorGUI.GetPropertyHeight(it);
} while(it.NextVisible(false));
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if(property.serializedObject.isEditingMultipleObjects)
{
EditorGUI.LabelField(position,k_MultiSplineEditMessage, EditorStyles.helpBox);
return;
}
label.text = L10n.Tr(k_SplineFoldoutTitle);
property.isExpanded = EditorGUI.Foldout(SplineUIManager.ReserveSpace(EditorGUIUtility.singleLineHeight, ref position), property.isExpanded, label);
if (property.isExpanded)
{
EditorGUI.indentLevel++;
var proxy = SplineUIManager.instance.GetProxyFromProperty(property);
var editTypeProperty = property.FindPropertyRelative("m_EditModeType");
EditorGUI.PropertyField(SplineUIManager.ReserveSpace(EditorGUI.GetPropertyHeight(editTypeProperty), ref position), editTypeProperty);
var pathProperty = proxy.SerializedObject.FindProperty("Spline");
// HACK to get around the fact that array size change isn't an actual change when applying (bug)
var knotsProperty = pathProperty.FindPropertyRelative("m_Knots");
var arraySize = knotsProperty.arraySize;
EditorGUI.BeginChangeCheck();
var it = pathProperty.Copy();
it.NextVisible(true);
do
{
EditorGUI.PropertyField(SplineUIManager.ReserveSpace(EditorGUI.GetPropertyHeight(it), ref position), it, true);
} while(it.NextVisible(false));
if(EditorGUI.EndChangeCheck() || arraySize != knotsProperty.arraySize)
{
SplineUIManager.instance.ApplyProxyToProperty(proxy, property);
}
EditorGUI.indentLevel--;
}
}
}
}

View file

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

View file

@ -0,0 +1,185 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
[CustomEditor(typeof(SplineExtrude))]
[CanEditMultipleObjects]
class SplineExtrudeEditor : SplineComponentEditor
{
SerializedProperty m_Container;
SerializedProperty m_RebuildOnSplineChange;
SerializedProperty m_RebuildFrequency;
SerializedProperty m_Sides;
SerializedProperty m_SegmentsPerUnit;
SerializedProperty m_Capped;
SerializedProperty m_Radius;
SerializedProperty m_Range;
SerializedProperty m_UpdateColliders;
static readonly GUIContent k_RangeContent = new GUIContent("Range", "The section of the Spline to extrude.");
static readonly GUIContent k_AdvancedContent = new GUIContent("Advanced", "Advanced Spline Extrude settings.");
static readonly GUIContent k_PercentageContent = new GUIContent("Percentage", "The section of the Spline to extrude in percentages.");
static readonly string k_Spline = "Spline";
static readonly string k_Geometry = L10n.Tr("Geometry");
static readonly string k_ProfileEdges = "Profile Edges";
static readonly string k_CapEnds = "Cap Ends";
static readonly string k_AutoRegenGeo = "Auto-Regen Geometry";
static readonly string k_To = L10n.Tr("to");
static readonly string k_From = L10n.Tr("from");
SplineExtrude[] m_Components;
bool m_AnyMissingMesh;
protected override void OnEnable()
{
base.OnEnable();
m_Container = serializedObject.FindProperty("m_Container");
m_RebuildOnSplineChange = serializedObject.FindProperty("m_RebuildOnSplineChange");
m_RebuildFrequency = serializedObject.FindProperty("m_RebuildFrequency");
m_Sides = serializedObject.FindProperty("m_Sides");
m_SegmentsPerUnit = serializedObject.FindProperty("m_SegmentsPerUnit");
m_Capped = serializedObject.FindProperty("m_Capped");
m_Radius = serializedObject.FindProperty("m_Radius");
m_Range = serializedObject.FindProperty("m_Range");
m_UpdateColliders = serializedObject.FindProperty("m_UpdateColliders");
m_Components = targets.Select(x => x as SplineExtrude).Where(y => y != null).ToArray();
m_AnyMissingMesh = m_Components.Any(x => x.TryGetComponent<MeshFilter>(out var filter) && filter.sharedMesh == null);
EditorSplineUtility.afterSplineWasModified += OnSplineModified;
}
void OnDisable()
{
EditorSplineUtility.afterSplineWasModified -= OnSplineModified;
}
void OnSplineModified(Spline spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var extrude in m_Components)
{
if (extrude.container != null && extrude.container.Spline == spline)
extrude.Rebuild();
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Container, new GUIContent(k_Spline, m_Container.tooltip));
HorizontalLine(Color.grey);
EditorGUILayout.LabelField(k_Geometry, EditorStyles.boldLabel);
if(m_AnyMissingMesh)
{
GUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(" ");
if(GUILayout.Button("Create Mesh Asset"))
CreateMeshAssets(m_Components);
GUILayout.EndHorizontal();
}
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Radius);
if(EditorGUI.EndChangeCheck())
m_Radius.floatValue = Mathf.Clamp(m_Radius.floatValue, .00001f, 1000f);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Sides, new GUIContent(k_ProfileEdges, m_Sides.tooltip));
if(EditorGUI.EndChangeCheck())
m_Sides.intValue = Mathf.Clamp(m_Sides.intValue, 3, 2048);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_SegmentsPerUnit);
if(EditorGUI.EndChangeCheck())
m_SegmentsPerUnit.floatValue = Mathf.Clamp(m_SegmentsPerUnit.floatValue, .00001f, 4096f);
EditorGUILayout.PropertyField(m_Capped, new GUIContent(k_CapEnds, m_Capped.tooltip));
EditorGUI.indentLevel--;
HorizontalLine(Color.grey);
m_Range.isExpanded = Foldout(m_Range.isExpanded, k_AdvancedContent);
if (m_Range.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUI.showMixedValue = m_Range.hasMultipleDifferentValues;
var range = m_Range.vector2Value;
EditorGUI.BeginChangeCheck();
EditorGUILayout.MinMaxSlider(k_RangeContent, ref range.x, ref range.y, 0f, 1f);
if (EditorGUI.EndChangeCheck())
m_Range.vector2Value = range;
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(k_PercentageContent);
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
EditorGUI.BeginChangeCheck();
var newRange = new Vector2(range.x, range.y);
using (new LabelWidthScope(30f))
newRange.x = EditorGUILayout.FloatField(k_From, range.x * 100f) / 100f;
using (new LabelWidthScope(15f))
newRange.y = EditorGUILayout.FloatField(k_To, range.y * 100f) / 100f;
if (EditorGUI.EndChangeCheck())
{
newRange.x = Mathf.Min(Mathf.Clamp(newRange.x, 0f, 1f), range.y);
newRange.y = Mathf.Max(newRange.x, Mathf.Clamp(newRange.y, 0f, 1f));
m_Range.vector2Value = newRange;
}
EditorGUILayout.EndHorizontal();
EditorGUI.showMixedValue = false;
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_RebuildOnSplineChange, new GUIContent(k_AutoRegenGeo, m_RebuildOnSplineChange.tooltip));
if (m_RebuildOnSplineChange.boolValue)
{
EditorGUI.indentLevel++;
EditorGUI.BeginDisabledGroup(!m_RebuildOnSplineChange.boolValue);
EditorGUILayout.PropertyField(m_RebuildFrequency);
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel--;
}
EditorGUILayout.PropertyField(m_UpdateColliders);
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
if(EditorGUI.EndChangeCheck())
foreach(var extrude in m_Components)
extrude.Rebuild();
}
void CreateMeshAssets(SplineExtrude[] components)
{
foreach (var extrude in components)
{
if (!extrude.TryGetComponent<MeshFilter>(out var filter) || filter.sharedMesh != null)
filter.sharedMesh = extrude.CreateMeshAsset();
}
m_AnyMissingMesh = false;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8ae5aa24d97048439e76e9392765c364
timeCreated: 1637689699

View file

@ -0,0 +1,54 @@
using UnityEditor.EditorTools;
using UnityEditor.Overlays;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
[Icon("UnityEditor.InspectorWindow")]
[Overlay(typeof(SceneView), "unity-spline-inspector", "Spline Inspector", "SplineInspector")]
sealed class SplineInspectorOverlay : Overlay, ITransientOverlay
{
static VisualTreeAsset s_VisualTree;
public bool visible => ToolManager.activeContextType == typeof(SplineToolContext);
ElementInspector m_ElementInspector;
public override VisualElement CreatePanelContent()
{
VisualElement root = new VisualElement();
m_ElementInspector = new ElementInspector();
UpdateInspector();
root.Add(m_ElementInspector);
return root;
}
public override void OnCreated()
{
displayedChanged += OnDisplayedChange;
SplineSelection.changed += UpdateInspector;
SplineConversionUtility.splineTypeChanged += UpdateInspector;
}
public override void OnWillBeDestroyed()
{
displayedChanged -= OnDisplayedChange;
SplineSelection.changed -= UpdateInspector;
SplineConversionUtility.splineTypeChanged -= UpdateInspector;
}
void OnDisplayedChange(bool displayed)
{
UpdateInspector();
}
void UpdateInspector()
{
m_ElementInspector?.SetElement(SplineSelection.GetActiveElement(), SplineSelection.count);
}
}
}

View file

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

View file

@ -0,0 +1,522 @@
using System;
using UnityEditor;
using UnityEditor.Splines;
using UnityEngine;
using UnityEngine.Splines;
class SplineInstantiateGizmoDrawer
{
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void DrawSplineInstantiateGizmos(SplineInstantiate scr, GizmoType gizmoType)
{
var instances = scr.instances;
foreach(var instance in instances)
{
var pos = instance.transform.position;
Handles.color = Color.red;
Handles.DrawAAPolyLine(3f,new []{ pos, pos + 0.25f * instance.transform.right });
Handles.color = Color.green;
Handles.DrawAAPolyLine(3f,new []{pos, pos + 0.25f * instance.transform.up});
Handles.color = Color.blue;
Handles.DrawAAPolyLine(3f,new []{pos, pos + 0.25f * instance.transform.forward});
}
}
}
[CustomPropertyDrawer (typeof(SplineInstantiate.InstantiableItem))]
class InstantiableItemDrawer : PropertyDrawer
{
static readonly string k_ProbabilityTooltip = L10n.Tr("Probability for that element to appear.");
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
var prefabProperty = property.FindPropertyRelative("prefab");
var probaProperty = property.FindPropertyRelative("probability");
var headerLine = ReserveSpace(EditorGUIUtility.singleLineHeight, ref rect);
using(new SplineInstantiateEditor.LabelWidthScope(0f))
EditorGUI.ObjectField(ReserveLineSpace(headerLine.width - 100, ref headerLine), prefabProperty, new GUIContent(""));
ReserveLineSpace(10, ref headerLine);
EditorGUI.LabelField(ReserveLineSpace(15, ref headerLine), new GUIContent("%", k_ProbabilityTooltip));
probaProperty.floatValue = EditorGUI.FloatField(ReserveLineSpace(60, ref headerLine), probaProperty.floatValue);
}
static Rect ReserveSpace(float height, ref Rect total)
{
Rect current = total;
current.height = height;
total.y += height;
return current;
}
static Rect ReserveLineSpace(float width, ref Rect total)
{
Rect current = total;
current.width = width;
total.x += width;
return current;
}
}
[CustomPropertyDrawer (typeof(SplineInstantiate.AlignAxis))]
class ItemAxisDrawer : PropertyDrawer
{
static int s_LastUpAxis;
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
var enumValue = property.intValue;
if(property.name == "m_Up")
{
property.intValue = (int)( (SplineInstantiate.AlignAxis)EditorGUI.EnumPopup(rect, label, (SplineInstantiate.AlignAxis)enumValue));
s_LastUpAxis = property.intValue;
}
else
{
property.intValue = (int)((SplineInstantiate.AlignAxis)EditorGUI.EnumPopup(rect, label, (SplineInstantiate.AlignAxis)enumValue,
(item) =>
{
int axisItem = (int)(SplineInstantiate.AlignAxis)item;
return !(axisItem == s_LastUpAxis || axisItem == (s_LastUpAxis + 3) % 6);
}));
}
}
}
[CustomEditor(typeof(SplineInstantiate),false)]
[CanEditMultipleObjects]
class SplineInstantiateEditor : SplineComponentEditor
{
enum SpawnType
{
Exact,
Random
}
SerializedProperty m_SplineContainer;
SerializedProperty m_ItemsToInstantiate;
SerializedProperty m_InstantiateMethod;
SerializedProperty m_Space;
SerializedProperty m_UpAxis;
SerializedProperty m_ForwardAxis;
SerializedProperty m_Spacing;
SerializedProperty m_PositionOffset;
SerializedProperty m_RotationOffset;
SerializedProperty m_ScaleOffset;
SerializedProperty m_AutoRefresh;
static readonly string[] k_SpacingTypesLabels = new []
{
L10n.Tr("Count"),
L10n.Tr("Spacing (Spline)"),
L10n.Tr("Spacing (Linear)")
};
static readonly string k_Helpbox = L10n.Tr("Instantiated Objects need a SplineContainer target to be created.");
//Setup Section
static readonly string k_Setup = L10n.Tr("Instantiated Object Setup");
static readonly string k_ObjectUp = L10n.Tr("Up Axis");
static readonly string k_ObjectUpTooltip = L10n.Tr("Object axis to use as Up Direction when instantiating on the Spline (default is Y).");
static readonly string k_ObjectForward = L10n.Tr("Forward Axis");
static readonly string k_ObjectForwardTooltip = L10n.Tr("Object axis to use as Forward Direction when instantiating on the Spline (default is Z).");
static readonly string k_AlignTo = L10n.Tr("Align To");
static readonly string k_AlignToTooltip = L10n.Tr("Define the space to use to orientate the instantiated object.");
static readonly string k_Instantiation = L10n.Tr("Instantiation");
static readonly string k_Method = L10n.Tr("Instantiate Method");
static readonly string k_MethodTooltip = L10n.Tr("How instances are generated along the spline.");
static readonly string k_Max = L10n.Tr("Max");
static readonly string k_Min = L10n.Tr("Min");
SpawnType m_SpacingType;
//Offsets
static readonly string k_Offset = L10n.Tr("Offsets");
static readonly string k_PositionOffset = L10n.Tr("Position Offset");
static readonly string k_PositionOffsetTooltip = L10n.Tr("Whether or not to use a position offset.");
static readonly string k_RotationOffset = L10n.Tr("Rotation Offset");
static readonly string k_RotationOffsetTooltip = L10n.Tr("Whether or not to use a rotation offset.");
static readonly string k_ScaleOffset = L10n.Tr("Scale Offset");
static readonly string k_ScaleOffsetTooltip = L10n.Tr("Whether or not to use a scale offset.");
//Generation
static readonly string k_Generation = L10n.Tr("Generation");
static readonly string k_AutoRefresh = L10n.Tr("Auto Refresh Generation");
static readonly string k_AutoRefreshTooltip = L10n.Tr("Automatically refresh the instances when the spline or the values are changed.");
static readonly string k_Randomize = L10n.Tr("Randomize");
static readonly string k_RandomizeTooltip = L10n.Tr("Compute a new randomization of the instances along the spline.");
static readonly string k_Regenerate = L10n.Tr("Regenerate");
static readonly string k_RegenerateTooltip = L10n.Tr("Regenerate the instances along the spline (only available when the content is not automatically refreshed).");
static readonly string k_Clear = L10n.Tr("Clear");
static readonly string k_ClearTooltip = L10n.Tr("Clear the instances along the spline (only available when the content is not automatically refreshed).");
bool m_PositionFoldout;
bool m_RotationFoldout;
bool m_ScaleFoldout;
enum OffsetType
{
Exact,
Random
};
protected override void OnEnable()
{
base.OnEnable();
m_SplineContainer = serializedObject.FindProperty("m_Container");
m_ItemsToInstantiate = serializedObject.FindProperty("m_ItemsToInstantiate");
m_InstantiateMethod = serializedObject.FindProperty("m_Method");
m_Space = serializedObject.FindProperty("m_Space");
m_UpAxis = serializedObject.FindProperty("m_Up");
m_ForwardAxis = serializedObject.FindProperty("m_Forward");
m_Spacing = serializedObject.FindProperty("m_Spacing");
m_PositionOffset = serializedObject.FindProperty("m_PositionOffset");
m_RotationOffset = serializedObject.FindProperty("m_RotationOffset");
m_ScaleOffset = serializedObject.FindProperty("m_ScaleOffset");
m_AutoRefresh = serializedObject.FindProperty("m_AutoRefresh");
m_SpacingType = m_Spacing.vector2Value.x == m_Spacing.vector2Value.y ? SpawnType.Exact : SpawnType.Random;
EditorSplineUtility.afterSplineWasModified += OnSplineModified;
}
void OnDisable()
{
EditorSplineUtility.afterSplineWasModified -= OnSplineModified;
}
void OnSplineModified(Spline spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
( (SplineInstantiate)target ).SetSplineDirty(spline);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
var splineInstantiate = ((SplineInstantiate)target);
var dirtyInstances = false;
var updateInstances = false;
EditorGUILayout.PropertyField(m_SplineContainer);
if(m_SplineContainer.objectReferenceValue == null)
EditorGUILayout.HelpBox(k_Helpbox, MessageType.Warning);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_ItemsToInstantiate);
dirtyInstances = EditorGUI.EndChangeCheck();
DoSetupSection();
dirtyInstances |= DoInstantiateSection();
updateInstances |= DisplayOffsets();
HorizontalLine( Color.grey );
EditorGUILayout.LabelField(k_Generation, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_AutoRefresh, new GUIContent(k_AutoRefresh, k_AutoRefreshTooltip));
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
EditorGUILayout.Separator();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
if(GUILayout.Button(new GUIContent(k_Randomize, k_RandomizeTooltip), GUILayout.MaxWidth(100f)))
splineInstantiate.Randomize();
if(GUILayout.Button(new GUIContent(k_Regenerate, k_RegenerateTooltip), GUILayout.MaxWidth(100f)))
updateInstances = true;
if(GUILayout.Button(new GUIContent(k_Clear, k_ClearTooltip), GUILayout.MaxWidth(100f)))
splineInstantiate.Clear();
EditorGUILayout.Space();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
if(dirtyInstances)
splineInstantiate.SetDirty();
if(updateInstances)
splineInstantiate.UpdateInstances();
}
void DoSetupSection()
{
HorizontalLine(Color.grey);
EditorGUILayout.LabelField(k_Setup, EditorStyles.boldLabel);
GUILayout.Space(5f);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_UpAxis, new GUIContent(k_ObjectUp, k_ObjectUpTooltip));
EditorGUILayout.PropertyField(m_ForwardAxis, new GUIContent(k_ObjectForward, k_ObjectForwardTooltip));
if(EditorGUI.EndChangeCheck())
{
//Insuring axis integrity
if(m_ForwardAxis.intValue == m_UpAxis.intValue || m_ForwardAxis.intValue == ( m_UpAxis.intValue + 3 ) % 6)
m_ForwardAxis.intValue = ( m_ForwardAxis.intValue + 1 ) % 6;
}
EditorGUILayout.PropertyField(m_Space, new GUIContent(k_AlignTo, k_AlignToTooltip));
EditorGUI.indentLevel--;
}
bool DoInstantiateSection()
{
var dirty = false;
Vector2 spacingV2 = m_Spacing.vector2Value;
HorizontalLine( Color.grey );
EditorGUILayout.LabelField(k_Instantiation, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_InstantiateMethod, new GUIContent(k_Method, k_MethodTooltip), EditorStyles.boldFont );
if(EditorGUI.EndChangeCheck())
{
if(m_SpacingType == SpawnType.Random && m_InstantiateMethod.intValue == (int)SplineInstantiate.Method.LinearDistance)
m_Spacing.vector2Value = new Vector2(spacingV2.x, float.NaN);
dirty = true;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(new GUIContent(k_SpacingTypesLabels[m_InstantiateMethod.intValue]));
EditorGUI.indentLevel--;
GUILayout.Space(2f);
EditorGUI.BeginChangeCheck();
float spacingX = m_Spacing.vector2Value.x;
var isExact = m_SpacingType == SpawnType.Exact;
if(isExact || m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
{
using(new LabelWidthScope(30f))
spacingX = (SplineInstantiate.Method)m_InstantiateMethod.intValue == SplineInstantiate.Method.InstanceCount ?
EditorGUILayout.IntField(new GUIContent(isExact ? L10n.Tr("Dist") : k_Min), (int)m_Spacing.vector2Value.x, GUILayout.MinWidth(50f)) :
EditorGUILayout.FloatField(new GUIContent(isExact ? L10n.Tr("Dist") : k_Min), m_Spacing.vector2Value.x, GUILayout.MinWidth(50f));
}
if(isExact)
{
spacingV2 = new Vector2(spacingX, spacingX);
}
else if(m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
{
using(new LabelWidthScope(30f))
{
var spacingY = (SplineInstantiate.Method)m_InstantiateMethod.intValue == SplineInstantiate.Method.InstanceCount ?
EditorGUILayout.IntField(new GUIContent(k_Max), (int)m_Spacing.vector2Value.y, GUILayout.MinWidth(50f)) :
EditorGUILayout.FloatField(new GUIContent(k_Max), m_Spacing.vector2Value.y, GUILayout.MinWidth(50f));
if(spacingX > m_Spacing.vector2Value.y)
spacingY = spacingX;
else if(spacingY < m_Spacing.vector2Value.x)
spacingX = spacingY;
spacingV2 = new Vector2(spacingX, spacingY);
}
}
if(EditorGUI.EndChangeCheck())
m_Spacing.vector2Value = spacingV2;
EditorGUI.BeginChangeCheck();
if(m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
m_SpacingType = (SpawnType)EditorGUILayout.EnumPopup(m_SpacingType, GUILayout.MinWidth(30f));
else
m_SpacingType = (SpawnType)EditorGUILayout.Popup(m_SpacingType == SpawnType.Exact ? 0 : 1,
new []{"Exact", "Auto"}, GUILayout.MinWidth(30f));
if(EditorGUI.EndChangeCheck())
{
if(m_SpacingType == SpawnType.Exact)
m_Spacing.vector2Value = new Vector2(spacingV2.x, spacingV2.x);
else if(m_InstantiateMethod.intValue == (int)SplineInstantiate.Method.LinearDistance)
m_Spacing.vector2Value = new Vector2(spacingV2.x, float.NaN);
dirty = true;
}
EditorGUILayout.EndHorizontal();
return dirty;
}
bool DoOffsetProperties(
SerializedProperty offsetProperty, GUIContent content, bool foldoutValue, out bool newFoldoutValue)
{
bool changed = false;
newFoldoutValue = foldoutValue;
EditorGUILayout.BeginHorizontal();
using(new LabelWidthScope(0f))
{
var setupProperty = offsetProperty.FindPropertyRelative("setup");
var setup = (SplineInstantiate.Vector3Offset.Setup)setupProperty.intValue;
var hasOffset = ( setup & SplineInstantiate.Vector3Offset.Setup.HasOffset ) != 0;
EditorGUI.BeginChangeCheck();
hasOffset = EditorGUILayout.Toggle(hasOffset, GUILayout.MaxWidth(20f));
if(EditorGUI.EndChangeCheck())
{
if(hasOffset)
setup |= SplineInstantiate.Vector3Offset.Setup.HasOffset;
else
setup &= ~SplineInstantiate.Vector3Offset.Setup.HasOffset;
setupProperty.intValue = (int)setup;
changed = true;
}
EditorGUILayout.Space(10f);
using(new EditorGUI.DisabledScope(!hasOffset))
{
newFoldoutValue = Foldout(foldoutValue, content, hasOffset) && hasOffset;
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
if(newFoldoutValue)
{
EditorGUILayout.BeginHorizontal();
var hasCustomSpace = ( setup & SplineInstantiate.Vector3Offset.Setup.HasCustomSpace ) != 0;
EditorGUI.BeginChangeCheck();
var space = m_Space.intValue < 1 ? "Spline Element" : m_Space.intValue == 1 ? "Spline Object" : "World";
hasCustomSpace = EditorGUILayout.Toggle(new GUIContent("Override space", L10n.Tr("Override current space (" + space + ")")), hasCustomSpace);
if(EditorGUI.EndChangeCheck())
{
if(hasCustomSpace)
setup |= SplineInstantiate.Vector3Offset.Setup.HasCustomSpace;
else
setup &= ~SplineInstantiate.Vector3Offset.Setup.HasCustomSpace;
setupProperty.intValue = (int)setup;
changed = true;
}
var spaceProperty = offsetProperty.FindPropertyRelative("space");
using(new EditorGUI.DisabledScope(!hasCustomSpace))
{
var type = (SplineInstantiate.OffsetSpace)spaceProperty.intValue;
EditorGUI.BeginChangeCheck();
type = (SplineInstantiate.OffsetSpace)EditorGUILayout.EnumPopup(type);
if(EditorGUI.EndChangeCheck())
{
spaceProperty.intValue = (int)type;
changed = true;
}
}
EditorGUILayout.EndHorizontal();
var minProperty = offsetProperty.FindPropertyRelative("min");
var maxProperty = offsetProperty.FindPropertyRelative("max");
var minPropertyValue = minProperty.vector3Value;
var maxPropertyValue = maxProperty.vector3Value;
float min, max;
SerializedProperty randomProperty;
for(int i = 0; i < 3; i++)
{
string label = i == 0 ? "X" : i == 1 ? "Y" : "Z";
EditorGUILayout.BeginHorizontal();
using(new LabelWidthScope(30f))
EditorGUILayout.LabelField(label);
randomProperty = offsetProperty.FindPropertyRelative("random"+label);
GUILayout.FlexibleSpace();
if(randomProperty.boolValue)
{
EditorGUI.BeginChangeCheck();
using(new LabelWidthScope(30f))
{
min = EditorGUILayout.FloatField("from", minPropertyValue[i], GUILayout.MinWidth(95f), GUILayout.MaxWidth(95f));
max = EditorGUILayout.FloatField(" to", maxPropertyValue[i], GUILayout.MinWidth(95f), GUILayout.MaxWidth(95f));
}
if(EditorGUI.EndChangeCheck())
{
if(min > maxPropertyValue[i])
maxPropertyValue[i] = min;
if(max < minPropertyValue[i])
minPropertyValue[i] = max;
minPropertyValue[i] = min;
maxPropertyValue[i] = max;
minProperty.vector3Value = minPropertyValue;
maxProperty.vector3Value = maxPropertyValue;
changed = true;
}
}
else
{
EditorGUI.BeginChangeCheck();
using(new LabelWidthScope(30f))
min = EditorGUILayout.FloatField("is ", minPropertyValue[i], GUILayout.MinWidth(193f), GUILayout.MaxWidth(193f));
if(EditorGUI.EndChangeCheck())
{
minPropertyValue[i] = min;
if(min > maxPropertyValue[i])
maxPropertyValue[i] = min;
minProperty.vector3Value = minPropertyValue;
maxProperty.vector3Value = maxPropertyValue;
changed = true;
}
}
EditorGUI.BeginChangeCheck();
var isOffsetRandom = randomProperty.boolValue ? OffsetType.Random : OffsetType.Exact;
using(new LabelWidthScope(0f))
isOffsetRandom = (OffsetType)EditorGUILayout.EnumPopup(isOffsetRandom,GUILayout.MinWidth(100f), GUILayout.MaxWidth(200f));
if(EditorGUI.EndChangeCheck())
{
randomProperty.boolValue = isOffsetRandom == OffsetType.Random;
changed = true;
}
EditorGUILayout.EndHorizontal();
}
}
}
}
return changed;
}
bool DisplayOffsets()
{
HorizontalLine( Color.grey );
var updateNeeded = DoOffsetProperties(m_PositionOffset, new GUIContent(k_PositionOffset, k_PositionOffsetTooltip), m_PositionFoldout, out m_PositionFoldout);
updateNeeded |= DoOffsetProperties(m_RotationOffset, new GUIContent(k_RotationOffset, k_RotationOffsetTooltip), m_RotationFoldout, out m_RotationFoldout);
updateNeeded |= DoOffsetProperties(m_ScaleOffset, new GUIContent(k_ScaleOffset, k_ScaleOffsetTooltip), m_ScaleFoldout, out m_ScaleFoldout);
return updateNeeded;
}
}

View file

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

View file

@ -0,0 +1,77 @@
using Unity.Mathematics;
using UnityEngine.Splines;
using UnityEngine;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.EditorTools;
#else
using ToolManager = UnityEditor.EditorTools.EditorTools;
#endif
namespace UnityEditor.Splines
{
static class SplineMenu
{
const string k_MenuPath = "GameObject/3D Object/Spline";
static GameObject CreateSplineGameObject(MenuCommand menuCommand, Spline spline = null)
{
var name = GameObjectUtility.GetUniqueNameForSibling(null, "Spline");
var gameObject = ObjectFactory.CreateGameObject(name, typeof(SplineContainer));
#if ST_EXPOSE_GO_CREATE_PLACEMENT_LANDED
ObjectFactory.PlaceGameObject(gameObject, menuCommand.context as GameObject);
#else
if (menuCommand.context is GameObject go)
{
Undo.RecordObject(gameObject.transform, "Re-parenting");
gameObject.transform.SetParent(go.transform);
}
#endif
if (spline != null)
{
var container = gameObject.GetComponent<SplineContainer>();
container.Spline = spline;
Selection.activeGameObject = gameObject;
}
return gameObject;
}
const int k_MenuPriority = 200;
[MenuItem(k_MenuPath + "/Draw Spline Tool...", false, k_MenuPriority + 0)]
static void CreateNewSpline(MenuCommand menuCommand)
{
var gameObject = CreateSplineGameObject(menuCommand);
gameObject.transform.localPosition = Vector3.zero;
gameObject.transform.localRotation = Quaternion.identity;
Selection.activeObject = gameObject;
ActiveEditorTracker.sharedTracker.RebuildIfNecessary();
//Ensuring trackers are rebuilt before changing to SplineContext
EditorApplication.delayCall += SetKnotPlacementTool;
}
static void SetKnotPlacementTool()
{
ToolManager.SetActiveContext<SplineToolContext>();
ToolManager.SetActiveTool<KnotPlacementTool>();
}
[MenuItem(k_MenuPath + "/Square", false, k_MenuPriority + 11)]
static void CreateSquare(MenuCommand command)
{
CreateSplineGameObject(command, SplineFactory.CreateSquare(1f));
}
[MenuItem(k_MenuPath + "/Circle", false, k_MenuPriority + 12)]
static void CreateCircle(MenuCommand command)
{
// .36 is just an eye-balled approximation
CreateSplineGameObject(command, SplineFactory.CreateRoundedSquare(1f, .36f));
}
}
}

View file

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

View file

@ -0,0 +1,31 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
[CustomPropertyDrawer(typeof(SplineType))]
sealed class SplineTypeDrawer : PropertyDrawer
{
static readonly List<IEditableSpline> s_PathsBuffer = new List<IEditableSpline>();
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginChangeCheck();
var index = EditorGUI.PropertyField(position, property, label);
if (EditorGUI.EndChangeCheck())
{
EditorApplication.delayCall += () =>
{
//When switching type we do a conversion pass to update the spline data to fit the new type
SplineConversionUtility.UpdateEditableSplinesForTargets(property.serializedObject.targetObjects);
EditableSplineUtility.GetSelectedSplines(property.serializedObject.targetObjects, s_PathsBuffer);
foreach (var path in s_PathsBuffer)
path.SetDirty();
SplineConversionUtility.ApplyEditableSplinesIfDirty(property.serializedObject.targetObjects);
SceneView.RepaintAll();
};
}
}
}
}

View file

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

View file

@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
sealed class SplineUIProxy : ScriptableObject
{
[NonSerialized] public SerializedObject SerializedObject;
[SerializeReference] public IEditableSpline Spline;
[NonSerialized] public int LastFrameCount;
}
class SplineUIManager : ScriptableSingleton<SplineUIManager>
{
List<BezierKnot> m_KnotsBuffer = new List<BezierKnot>();
Dictionary<Spline, SplineUIProxy> m_Proxies = new Dictionary<Spline, SplineUIProxy>();
List<Spline> m_ProxiesToDestroy = new List<Spline>();
void OnEnable()
{
Selection.selectionChanged += VerifyProxiesAreValid;
#if UNITY_EDITOR
EditorSplineUtility.afterSplineWasModified += OnSplineUpdated;
Undo.undoRedoPerformed += ClearProxies;
#endif
}
void OnDisable()
{
Selection.selectionChanged -= VerifyProxiesAreValid;
#if UNITY_EDITOR
EditorSplineUtility.afterSplineWasModified -= OnSplineUpdated;
Undo.undoRedoPerformed -= ClearProxies;
#endif
}
public SplineUIProxy GetProxyFromProperty(SerializedProperty splineProperty)
{
var targetSpline = GetSplineValue(splineProperty.serializedObject.targetObject, splineProperty.propertyPath);
if (targetSpline == null || !m_Proxies.TryGetValue(targetSpline, out SplineUIProxy proxy))
{
proxy = ScriptableObject.CreateInstance<SplineUIProxy>();
var editType = splineProperty.FindPropertyRelative("m_EditModeType");
if (splineProperty.hasMultipleDifferentValues)
{
proxy.Spline = null;
}
else
{
IEditableSpline spline = EditableSplineUtility.CreatePathOfType((SplineType) editType.enumValueIndex);
spline.closed = splineProperty.FindPropertyRelative("m_Closed").boolValue;
var knotsProperty = splineProperty.FindPropertyRelative("m_Knots");
m_KnotsBuffer.Clear();
for (int i = 0; i < knotsProperty.arraySize; ++i)
{
var knot = knotsProperty.GetArrayElementAtIndex(i);
m_KnotsBuffer.Add(new BezierKnot(
GetFloat3FromProperty(knot.FindPropertyRelative("Position")),
GetFloat3FromProperty(knot.FindPropertyRelative("TangentIn")),
GetFloat3FromProperty(knot.FindPropertyRelative("TangentOut")),
GetQuaternionFromProperty(knot.FindPropertyRelative("Rotation"))));
}
spline.FromBezier(m_KnotsBuffer);
proxy.Spline = spline;
var conversionData = spline;
conversionData.isDirty = false;
conversionData.ValidateData();
}
proxy.SerializedObject = new SerializedObject(proxy);
if(targetSpline != null)
m_Proxies.Add(targetSpline, proxy);
}
proxy.LastFrameCount = Time.frameCount;
return proxy;
}
public void ApplyProxyToProperty(SplineUIProxy proxy, SerializedProperty property)
{
proxy.SerializedObject.ApplyModifiedPropertiesWithoutUndo();
var path = proxy.Spline;
path.ValidateData();
property.FindPropertyRelative("m_EditModeType").enumValueIndex = (int)EditableSplineUtility.GetSplineType(path);
property.FindPropertyRelative("m_Closed").boolValue = proxy.SerializedObject.FindProperty("Spline.m_Closed").boolValue;
var knotsProperty = property.FindPropertyRelative("m_Knots");
m_KnotsBuffer.Clear();
path.ToBezier(m_KnotsBuffer);
knotsProperty.arraySize = m_KnotsBuffer.Count;
for (int i = 0; i < m_KnotsBuffer.Count; ++i)
{
var knotProperty = knotsProperty.GetArrayElementAtIndex(i);
var knot = m_KnotsBuffer[i];
SetFloat3Property(knotProperty.FindPropertyRelative("Position"), knot.Position);
SetFloat3Property(knotProperty.FindPropertyRelative("TangentIn"), knot.TangentIn);
SetFloat3Property(knotProperty.FindPropertyRelative("TangentOut"), knot.TangentOut);
if(math.length(knot.Rotation.value) == 0)
{
//Update knot rotation with a valid value
knot.Rotation = quaternion.identity;
m_KnotsBuffer[i] = knot;
//Updating proxy
path.FromBezier(m_KnotsBuffer);
}
SetQuaternionFromProperty(knotProperty.FindPropertyRelative("Rotation"), knot.Rotation);
}
var targetSpline = GetSplineValue(property.serializedObject.targetObject, property.propertyPath);
targetSpline?.SetDirty();
property.FindPropertyRelative("m_Length").floatValue = -1;
EditorApplication.delayCall += () => SplineConversionUtility.UpdateEditableSplinesForTarget(property.serializedObject.targetObject);
}
Spline GetSplineValue(UnityEngine.Object targetObject, string propertyPath)
{
if(targetObject == null)
return null;
var fieldInfo = targetObject.GetType().GetField(propertyPath, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
return (Spline)fieldInfo?.GetValue(targetObject);;
}
static float3 GetFloat3FromProperty(SerializedProperty property)
{
return new float3(
property.FindPropertyRelative("x").floatValue,
property.FindPropertyRelative("y").floatValue,
property.FindPropertyRelative("z").floatValue);
}
static void SetFloat3Property(SerializedProperty property, float3 value)
{
property.FindPropertyRelative("x").floatValue = value.x;
property.FindPropertyRelative("y").floatValue = value.y;
property.FindPropertyRelative("z").floatValue = value.z;
}
static quaternion GetQuaternionFromProperty(SerializedProperty property)
{
return new quaternion(
property.FindPropertyRelative("value.x").floatValue,
property.FindPropertyRelative("value.y").floatValue,
property.FindPropertyRelative("value.z").floatValue,
property.FindPropertyRelative("value.w").floatValue);
}
static void SetQuaternionFromProperty(SerializedProperty property, quaternion quaternion)
{
property.FindPropertyRelative("value.x").floatValue = quaternion.value.x;
property.FindPropertyRelative("value.y").floatValue = quaternion.value.y;
property.FindPropertyRelative("value.z").floatValue = quaternion.value.z;
property.FindPropertyRelative("value.w").floatValue = quaternion.value.w;
}
void ClearProxies()
{
foreach(var kvp in m_Proxies)
{
//Forcing inspector update on spline changes in the scene view
DestroyImmediate(kvp.Value);
}
m_Proxies.Clear();
}
void OnSplineUpdated(Spline spline)
{
if(m_Proxies.TryGetValue(spline, out SplineUIProxy proxy))
{
//Forcing inspector update on spline changes in the scene view
DestroyImmediate(proxy);
m_Proxies.Remove(spline);
}
}
void VerifyProxiesAreValid()
{
if(m_Proxies.Count == 0)
return;
m_ProxiesToDestroy.Clear();
var currentTime = Time.frameCount;
const int frameCountBeforeProxyRemoval = 5;
foreach(var kvp in m_Proxies)
{
if(currentTime - kvp.Value.LastFrameCount > frameCountBeforeProxyRemoval)
m_ProxiesToDestroy.Add(kvp.Key);
}
foreach(var keyToDestroy in m_ProxiesToDestroy)
{
if (m_Proxies.TryGetValue(keyToDestroy, out SplineUIProxy proxy))
{
DestroyImmediate(proxy);
m_Proxies.Remove(keyToDestroy);
}
}
}
internal static Rect ReserveSpace(float height, ref Rect total)
{
Rect current = total;
current.height = height;
total.y += height;
return current;
}
}
}

View file

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

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d96dfe6cb862ab34894ef767abef2e84
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 80c4e990f69fadf42a06e238da57fbfd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

View file

@ -0,0 +1,181 @@
fileFormatVersion: 2
guid: 954ac4bd24b73a540a2e84414caf856c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: tvOS
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Lumin
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: CloudRendering
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

View file

@ -0,0 +1,181 @@
fileFormatVersion: 2
guid: fdacd376014f8b743a9561d5652ca823
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: tvOS
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Lumin
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: CloudRendering
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View file

@ -0,0 +1,147 @@
fileFormatVersion: 2
guid: 3735bf6bd1171ad43896aaea4a3b417f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 2
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

View file

@ -0,0 +1,147 @@
fileFormatVersion: 2
guid: 74e0f4cadf762ff468bb8bb11c2ca3c7
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 2
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

View file

@ -0,0 +1,181 @@
fileFormatVersion: 2
guid: fbb7a4425fc8e4e5bb4f932ae7f52073
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: tvOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Lumin
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: CloudRendering
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

View file

@ -0,0 +1,181 @@
fileFormatVersion: 2
guid: 2ad42e04f6841ea64a7f21707d1aec22
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: tvOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Lumin
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: CloudRendering
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

View file

@ -0,0 +1,109 @@
fileFormatVersion: 2
guid: ce960f170ac1c3a4488973661e687f09
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

View file

@ -0,0 +1,109 @@
fileFormatVersion: 2
guid: 4099a528d97ba9f4ca8107e0144b3c91
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show more