1062 lines
42 KiB
C#
1062 lines
42 KiB
C#
namespace Dreamteck.Splines.Editor
|
|
{
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Dreamteck.Editor;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using Dreamteck.Splines;
|
|
|
|
public class SplineEditor : SplineEditorBase
|
|
{
|
|
public enum Space { World, Local };
|
|
public bool editMode = false;
|
|
protected Matrix4x4 _matrix;
|
|
protected virtual string editorName { get { return "SplineEditor"; } }
|
|
|
|
public bool is2D = false;
|
|
public Color drawColor = Color.white;
|
|
|
|
public MainPointModule mainModule;
|
|
public SerializedSplinePoint[] points = new SerializedSplinePoint[0];
|
|
public List<int> selectedPoints = new List<int>();
|
|
public Tool lastEditorTool = Tool.None;
|
|
public Space editSpace = Space.World;
|
|
|
|
public delegate void SplineEvaluation(double percent, ref SplineSample result);
|
|
public delegate void SplinePointEvaluation(int pointIndex, ref SplineSample result);
|
|
public delegate Vector3 SplineEvaluatePosition(double percent);
|
|
public delegate float SplineCalculateLength(double from, double to);
|
|
public delegate double SplineTravel(double start, float distance, Spline.Direction direction);
|
|
|
|
public SplineEvaluation evaluate;
|
|
public SplinePointEvaluation evaluateAtPoint;
|
|
public SplineEvaluatePosition evaluatePosition;
|
|
public SplineCalculateLength calculateLength;
|
|
public SplineTravel travel;
|
|
public EmptyHandler selectionChangeHandler;
|
|
|
|
public int moduleCount
|
|
{
|
|
get { return _modules.Length; }
|
|
}
|
|
|
|
public PointModule currentModule
|
|
{
|
|
get
|
|
{
|
|
if (_module < 0 || _module >= _modules.Length) return null;
|
|
else return _modules[_module];
|
|
}
|
|
}
|
|
|
|
protected List<PointOperation> pointOperations = new List<PointOperation>();
|
|
protected Vector2 lastClickPoint = Vector2.zero;
|
|
protected GUIContent[] toolContents = new GUIContent[0], toolContentsSelected = new GUIContent[0];
|
|
protected bool pointToolsToggle = false;
|
|
protected Toolbar toolbar;
|
|
protected SplineSample evalResult = new SplineSample();
|
|
protected SerializedProperty _splineProperty { get; private set; }
|
|
protected SerializedProperty _pointsProperty { get; private set; }
|
|
protected SerializedProperty _typeProperty { get; private set; }
|
|
protected SerializedProperty _sampleRateProperty { get; private set; }
|
|
protected SerializedProperty _closedProperty { get; private set; }
|
|
protected string splinePropertyName
|
|
{
|
|
get
|
|
{
|
|
if (_customSplinePropertyName != "") return _customSplinePropertyName;
|
|
return "_spline";
|
|
}
|
|
}
|
|
|
|
private int _module = -1, _selectModule = -1, _loadedModuleIndex = -1;
|
|
private PointModule[] _modules = new PointModule[0];
|
|
private string[] _pointOperationStrings = new string[0];
|
|
private float _editLabelAlpha = 0f;
|
|
private Vector2 _editLabelPosition = Vector2.zero;
|
|
private float lastEmptyClickTime = 0f;
|
|
private int _selectedPointOperation = 0;
|
|
private bool _emptyClick = false;
|
|
private string _customSplinePropertyName = "";
|
|
|
|
public Matrix4x4 matrix
|
|
{
|
|
get { return _matrix; }
|
|
}
|
|
|
|
public SplineEditor(Matrix4x4 transformMatrix, SerializedObject splineHolder, string customSplinePropertyName) : base(splineHolder)
|
|
{
|
|
_customSplinePropertyName = customSplinePropertyName;
|
|
Initialize(transformMatrix, splineHolder);
|
|
}
|
|
|
|
|
|
public SplineEditor(Matrix4x4 transformMatrix, SerializedObject splineHolder) : base(splineHolder)
|
|
{
|
|
Initialize(transformMatrix, splineHolder);
|
|
}
|
|
|
|
private void Initialize(Matrix4x4 transformMatrix, SerializedObject splineHolder)
|
|
{
|
|
_matrix = transformMatrix;
|
|
string[] serializedPath = splinePropertyName.Split('/');
|
|
foreach (var element in serializedPath)
|
|
{
|
|
if (_splineProperty == null)
|
|
{
|
|
_splineProperty = serializedObject.FindProperty(element);
|
|
continue;
|
|
}
|
|
int i = 0;
|
|
if (int.TryParse(element, out i))
|
|
{
|
|
_splineProperty = _splineProperty.GetArrayElementAtIndex(i);
|
|
}
|
|
else
|
|
{
|
|
_splineProperty = _splineProperty.FindPropertyRelative(element);
|
|
}
|
|
}
|
|
|
|
GetSerializedProperteis();
|
|
mainModule = new MainPointModule(this);
|
|
mainModule.onSelectionChanged += OnSelectionChanged;
|
|
List<PointModule> moduleList = new List<PointModule>();
|
|
OnModuleList(moduleList);
|
|
_modules = moduleList.ToArray();
|
|
toolContents = new GUIContent[_modules.Length];
|
|
toolContentsSelected = new GUIContent[_modules.Length];
|
|
for (int i = 0; i < _modules.Length; i++)
|
|
{
|
|
_modules[i].onSelectionChanged += OnSelectionChanged;
|
|
toolContents[i] = _modules[i].GetIconOff();
|
|
toolContentsSelected[i] = _modules[i].GetIconOn();
|
|
}
|
|
toolbar = new Toolbar(toolContents, toolContentsSelected, 35f);
|
|
|
|
pointOperations.Add(new PointOperation { name = "Flat X", action = delegate { FlatSelection(0); } });
|
|
pointOperations.Add(new PointOperation { name = "Flat Y", action = delegate { FlatSelection(1); } });
|
|
pointOperations.Add(new PointOperation { name = "Flat Z", action = delegate { FlatSelection(2); } });
|
|
pointOperations.Add(new PointOperation { name = "Mirror X", action = delegate { MirrorSelection(0); } });
|
|
pointOperations.Add(new PointOperation { name = "Mirror Y", action = delegate { MirrorSelection(1); } });
|
|
pointOperations.Add(new PointOperation { name = "Mirror Z", action = delegate { MirrorSelection(2); } });
|
|
pointOperations.Add(new PointOperation { name = "Distribute Evenly", action = delegate { DistributeEvenly(); } });
|
|
pointOperations.Add(new PointOperation { name = "Auto Bezier Tangents", action = delegate { AutoTangents(); } });
|
|
pointOperations.Add(new PointOperation { name = "Swap Bezier Tangents", action = delegate { SwapTangents(); } });
|
|
pointOperations.Add(new PointOperation { name = "Flip Bezier Tangents", action = delegate { FlipTangents(); } });
|
|
pointOperations.Add(new PointOperation { name = "Flip First Bezier Tangent", action = delegate { FlipFirstTangent(); } });
|
|
pointOperations.Add(new PointOperation { name = "Flip Seconds Bezier Tangent", action = delegate { FlipSecondTangent(); } });
|
|
|
|
_pointOperationStrings = new string[pointOperations.Count];
|
|
for (int i = 0; i < pointOperations.Count; i++)
|
|
{
|
|
_pointOperationStrings[i] = pointOperations[i].name;
|
|
}
|
|
if (_selectedPointOperation >= _pointOperationStrings.Length || _selectedPointOperation < 0)
|
|
{
|
|
_selectedPointOperation = 0;
|
|
}
|
|
}
|
|
|
|
protected virtual void GetSerializedProperteis()
|
|
{
|
|
_pointsProperty = _splineProperty.FindPropertyRelative("points");
|
|
_typeProperty = _splineProperty.FindPropertyRelative("type");
|
|
_sampleRateProperty = _splineProperty.FindPropertyRelative("sampleRate");
|
|
_closedProperty = _splineProperty.FindPropertyRelative("closed");
|
|
}
|
|
|
|
public PointModule GetModule(int index)
|
|
{
|
|
return _modules[index];
|
|
}
|
|
|
|
public override void UndoRedoPerformed()
|
|
{
|
|
GetPointsFromSpline();
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
if(selectedPoints[i] >= points.Length)
|
|
{
|
|
selectedPoints.RemoveAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
protected virtual void OnModuleList(List<PointModule> list)
|
|
{
|
|
list.Add(new CreatePointModule(this));
|
|
list.Add(new DeletePointModule(this));
|
|
list.Add(new PointMoveModule(this));
|
|
list.Add(new PointRotateModule(this));
|
|
list.Add(new PointScaleModule(this));
|
|
list.Add(new PointNormalModule(this));
|
|
list.Add(new PointMirrorModule(this));
|
|
}
|
|
|
|
public virtual void GetPointsFromSpline()
|
|
{
|
|
serializedObject.Update();
|
|
if (points.Length != _pointsProperty.arraySize)
|
|
{
|
|
points = new SerializedSplinePoint[_pointsProperty.arraySize];
|
|
}
|
|
|
|
for (int i = 0; i < _pointsProperty.arraySize; i++)
|
|
{
|
|
points[i] = new SerializedSplinePoint(_pointsProperty.GetArrayElementAtIndex(i));
|
|
}
|
|
}
|
|
|
|
public virtual void ApplyModifiedProperties(bool forceAllUpdate = false)
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
public virtual void SetPreviewPoints(SplinePoint[] points)
|
|
{
|
|
|
|
}
|
|
|
|
public SplinePoint[] GetPointsArray()
|
|
{
|
|
SplinePoint[] p = new SplinePoint[points.Length];
|
|
for (int i = 0; i < p.Length; i++)
|
|
{
|
|
p[i] = points[i].CreateSplinePoint();
|
|
}
|
|
return p;
|
|
}
|
|
|
|
public void SetPointsArray(SplinePoint[] input)
|
|
{
|
|
SetPointsCount(input.Length);
|
|
for (int i = 0; i < points.Length; i++)
|
|
{
|
|
points[i].SetPoint(input[i]);
|
|
}
|
|
}
|
|
|
|
public void SetPointsCount(int count)
|
|
{
|
|
_pointsProperty.arraySize = count;
|
|
serializedObject.ApplyModifiedProperties();
|
|
GetPointsFromSpline();
|
|
}
|
|
|
|
public virtual void DeletePoint(int index)
|
|
{
|
|
_pointsProperty.DeleteArrayElementAtIndex(index);
|
|
ApplyModifiedProperties(true);
|
|
GetPointsFromSpline();
|
|
}
|
|
|
|
public void AddPointAt(int index)
|
|
{
|
|
_pointsProperty.InsertArrayElementAtIndex(index);
|
|
ApplyModifiedProperties(true);
|
|
GetPointsFromSpline();
|
|
}
|
|
|
|
|
|
|
|
public override void Destroy()
|
|
{
|
|
base.Destroy();
|
|
mainModule.Deselect();
|
|
if (currentModule != null) currentModule.Deselect();
|
|
if(lastEditorTool != Tool.None && Tools.current == Tool.None) Tools.current = lastEditorTool;
|
|
}
|
|
|
|
public virtual void SetSplineClosed(bool closed)
|
|
{
|
|
if (points.Length < 3)
|
|
{
|
|
closed = false;
|
|
}
|
|
_closedProperty.boolValue = closed;
|
|
}
|
|
|
|
public virtual void SetSplineType(Spline.Type type)
|
|
{
|
|
_typeProperty.enumValueIndex = (int)type;
|
|
}
|
|
|
|
public virtual void SetSplineSampleRate(int rate)
|
|
{
|
|
if (rate < 2) rate = 2;
|
|
_sampleRateProperty.intValue = rate;
|
|
}
|
|
|
|
public virtual bool GetSplineClosed()
|
|
{
|
|
return _closedProperty.boolValue;
|
|
}
|
|
|
|
public virtual int GetSplineSampleRate()
|
|
{
|
|
return _sampleRateProperty.intValue;
|
|
}
|
|
|
|
public virtual Spline.Type GetSplineType()
|
|
{
|
|
return (Spline.Type)_typeProperty.enumValueIndex;
|
|
}
|
|
|
|
void OnSelectionChanged()
|
|
{
|
|
ResetCurrentModule();
|
|
Repaint();
|
|
if (selectionChangeHandler != null) selectionChangeHandler();
|
|
}
|
|
|
|
protected override void Save()
|
|
{
|
|
base.Save();
|
|
EditorPrefs.SetBool(GetSaveName("editMode"), editMode);
|
|
EditorPrefs.SetBool(GetSaveName("pointToolsToggle"), pointToolsToggle);
|
|
EditorPrefs.SetInt(GetSaveName("selectedPointOperation"), _selectedPointOperation);
|
|
}
|
|
|
|
protected override void Load()
|
|
{
|
|
base.Load();
|
|
editMode = EditorPrefs.GetBool(GetSaveName("editMode"), false);
|
|
pointToolsToggle = EditorPrefs.GetBool(GetSaveName("pointToolsToggle"), false);
|
|
_selectedPointOperation = EditorPrefs.GetInt(GetSaveName("selectedPointOperation"), 0);
|
|
}
|
|
|
|
private void HandleEditModeToggle()
|
|
{
|
|
if(Event.current.type == EventType.KeyDown)
|
|
{
|
|
if (editMode && Event.current.keyCode == KeyCode.Escape)
|
|
{
|
|
if(_module >= 0)
|
|
{
|
|
UntoggleCurrentModule();
|
|
Repaint();
|
|
} else
|
|
{
|
|
editMode = false;
|
|
Repaint();
|
|
}
|
|
}
|
|
if (Event.current.control && Event.current.keyCode == KeyCode.E) {
|
|
editMode = !editMode;
|
|
Repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void DrawInspector()
|
|
{
|
|
GetPointsFromSpline();
|
|
HandleEditModeToggle();
|
|
base.DrawInspector();
|
|
if (editMode)
|
|
{
|
|
if (!gizmosEnabled)
|
|
{
|
|
EditorGUILayout.HelpBox("Gizmos are disabled in the scene view. Enable Gizmos in the scene view for the spline editor to work.", MessageType.Error);
|
|
}
|
|
EditorGUILayout.Space();
|
|
DrawToolMenu();
|
|
EditorGUILayout.Space();
|
|
EditorGUI.BeginChangeCheck();
|
|
if (currentModule != null)
|
|
{
|
|
currentModule.DrawInspector();
|
|
if (currentModule.hasChanged)
|
|
{
|
|
ApplyModifiedProperties();
|
|
}
|
|
}
|
|
DreamteckEditorGUI.DrawSeparator();
|
|
PointPanel();
|
|
if (EditorGUI.EndChangeCheck()) ResetCurrentModule();
|
|
} else
|
|
{
|
|
if (GUILayout.Button("Edit"))
|
|
{
|
|
editMode = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawToolMenu()
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (_loadedModuleIndex >= 0)
|
|
{
|
|
ToggleModule(_loadedModuleIndex);
|
|
_loadedModuleIndex = -1;
|
|
}
|
|
_selectModule = _module;
|
|
EditorGUI.BeginChangeCheck();
|
|
toolbar.Draw(ref _selectModule);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
ToggleModule(_selectModule);
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
protected virtual void PointPanel()
|
|
{
|
|
if (points.Length == 0)
|
|
{
|
|
EditorGUILayout.LabelField("No control points available.", EditorStyles.centeredGreyMiniLabel);
|
|
return;
|
|
}
|
|
mainModule.DrawInspector();
|
|
if (mainModule.hasChanged)
|
|
{
|
|
ApplyModifiedProperties();
|
|
}
|
|
if (selectedPoints.Count > 0 && points.Length > 0)
|
|
{
|
|
PointMenu();
|
|
}
|
|
}
|
|
|
|
public virtual void BeforeSceneGUI(SceneView current)
|
|
{
|
|
mainModule.BeforeSceneDraw(current);
|
|
if (_module >= 0 && _module < _modules.Length)
|
|
{
|
|
_modules[_module].BeforeSceneDraw(current);
|
|
}
|
|
}
|
|
|
|
public override void DrawScene(SceneView current)
|
|
{
|
|
GetPointsFromSpline();
|
|
HandleEditModeToggle();
|
|
if (!editMode)
|
|
{
|
|
return;
|
|
}
|
|
base.DrawScene(current);
|
|
Event e = Event.current;
|
|
if (Tools.current != Tool.None)
|
|
{
|
|
lastEditorTool = Tools.current;
|
|
Tools.current = Tool.None;
|
|
}
|
|
int controlID = GUIUtility.GetControlID(FocusType.Passive);
|
|
if (e.GetTypeForControl(controlID) == EventType.Layout) HandleUtility.AddDefaultControl(controlID);
|
|
|
|
if (eventModule.mouseLeftDown) lastClickPoint = e.mousePosition;
|
|
EditorGUI.BeginChangeCheck();
|
|
mainModule.DrawScene();
|
|
if (mainModule.hasChanged)
|
|
{
|
|
ApplyModifiedProperties();
|
|
}
|
|
if (currentModule != null)
|
|
{
|
|
currentModule.DrawScene();
|
|
if (currentModule.hasChanged)
|
|
{
|
|
ApplyModifiedProperties();
|
|
}
|
|
if (currentModule is CreatePointModule)
|
|
{
|
|
if (eventModule.mouseLeftDown && eventModule.mouseRight)
|
|
{
|
|
GUIUtility.hotControl = -1;
|
|
ApplyModifiedProperties(true);
|
|
ToggleModule(0);
|
|
}
|
|
}
|
|
}
|
|
if(eventModule.mouseLeftDown) _emptyClick = GUIUtility.hotControl == 0;
|
|
|
|
if (_emptyClick)
|
|
{
|
|
if (eventModule.mouseLeft && !mainModule.isDragging && Vector2.Distance(lastClickPoint, e.mousePosition) >= mainModule.minimumRectSize && !eventModule.alt)
|
|
{
|
|
mainModule.StartDrag(lastClickPoint);
|
|
_emptyClick = false;
|
|
}
|
|
}
|
|
|
|
if (eventModule.mouseLeftUp)
|
|
{
|
|
if (mainModule.isDragging) mainModule.FinishDrag();
|
|
else
|
|
{
|
|
if (_emptyClick && !eventModule.alt)
|
|
{
|
|
if(selectedPoints.Count > 0) mainModule.ClearSelection();
|
|
else if(editMode)
|
|
{
|
|
if (Time.realtimeSinceStartup - lastEmptyClickTime <= 0.3f)
|
|
{
|
|
editMode = false;
|
|
}
|
|
else
|
|
{
|
|
_editLabelAlpha = 1f;
|
|
_editLabelPosition = e.mousePosition;
|
|
lastEmptyClickTime = Time.realtimeSinceStartup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (!eventModule.mouseRight && !eventModule.mouseLeft && e.type == EventType.KeyDown && !e.control)
|
|
{
|
|
switch (e.keyCode)
|
|
{
|
|
case KeyCode.Q:
|
|
if (_module == 0) ToggleModule(1);
|
|
else ToggleModule(0);
|
|
e.Use(); break;
|
|
case KeyCode.W: ToggleModule(2); e.Use(); break;
|
|
case KeyCode.E: ToggleModule(3); e.Use(); break;
|
|
case KeyCode.R: ToggleModule(4); e.Use(); break;
|
|
case KeyCode.T: ToggleModule(5); e.Use(); break;
|
|
case KeyCode.Y: ToggleModule(6); e.Use(); break;
|
|
}
|
|
}
|
|
|
|
if(_editLabelAlpha > 0f)
|
|
{
|
|
Handles.BeginGUI();
|
|
GUI.contentColor = new Color(1f, 1f, 1f, _editLabelAlpha);
|
|
DreamteckEditorGUI.Label(new Rect(_editLabelPosition, new Vector2(140, 50)), "Click Again To Exit");
|
|
Handles.EndGUI();
|
|
_editLabelAlpha = Mathf.MoveTowards(_editLabelAlpha, 0f, Time.deltaTime * 0.05f);
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
public void ToggleModule(int index)
|
|
{
|
|
Tools.current = Tool.None;
|
|
if (currentModule != null) currentModule.Deselect();
|
|
if (index == _module) _module = -1;
|
|
else
|
|
{
|
|
_module = index;
|
|
ResetCurrentModule();
|
|
currentModule.Select();
|
|
if (currentModule.hasChanged)
|
|
{
|
|
ApplyModifiedProperties(true);
|
|
}
|
|
}
|
|
Repaint();
|
|
}
|
|
|
|
public void UntoggleCurrentModule()
|
|
{
|
|
if (currentModule != null) currentModule.Deselect();
|
|
_module = -1;
|
|
Repaint();
|
|
}
|
|
|
|
|
|
protected virtual void PointMenu()
|
|
{
|
|
//Otherwise show the editing menu + the point selection menu
|
|
Vector3 avgPos = Vector3.zero;
|
|
Vector3 avgTan = Vector3.zero;
|
|
Vector3 avgTan2 = Vector3.zero;
|
|
Vector3 avgNormal = Vector3.zero;
|
|
float avgSize = 0f;
|
|
Color avgColor = Color.clear;
|
|
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
avgPos += points[selectedPoints[i]].position;
|
|
avgNormal += points[selectedPoints[i]].normal;
|
|
avgSize += points[selectedPoints[i]].size;
|
|
avgTan += points[selectedPoints[i]].tangent;
|
|
avgTan2 += points[selectedPoints[i]].tangent2;
|
|
avgColor += points[selectedPoints[i]].color;
|
|
}
|
|
|
|
avgPos /= selectedPoints.Count;
|
|
avgTan /= selectedPoints.Count;
|
|
avgTan2 /= selectedPoints.Count;
|
|
avgSize /= selectedPoints.Count;
|
|
avgColor /= selectedPoints.Count;
|
|
avgNormal.Normalize();
|
|
|
|
SplinePoint avgPoint = new SplinePoint(avgPos, avgPos);
|
|
avgPoint.tangent = avgTan;
|
|
avgPoint.tangent2 = avgTan2;
|
|
avgPoint.size = avgSize;
|
|
avgPoint.color = avgColor;
|
|
avgPoint.type = points[selectedPoints[0]].type;
|
|
SplinePoint.Type lastType = avgPoint.type;
|
|
|
|
avgPoint.normal = avgNormal;
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Point Operations");
|
|
|
|
EditorGUILayout.BeginVertical();
|
|
|
|
_selectedPointOperation = EditorGUILayout.Popup(_selectedPointOperation, _pointOperationStrings);
|
|
if (GUILayout.Button("Apply"))
|
|
{
|
|
pointOperations[_selectedPointOperation].action.Invoke();
|
|
ApplyModifiedProperties();
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
editSpace = (Space)EditorGUILayout.EnumPopup("Coordinate Space", editSpace);
|
|
bool isBezier = _typeProperty.enumValueIndex == (int)Spline.Type.Bezier;
|
|
if (isBezier)
|
|
{
|
|
if (is2D)
|
|
{
|
|
avgPoint.SetTangentPosition(TransformedPositionField2D("Tangent 1", avgPoint.tangent));
|
|
avgPoint.SetTangent2Position(TransformedPositionField2D("Tangent 2", avgPoint.tangent2));
|
|
}
|
|
else
|
|
{
|
|
avgPoint.SetTangentPosition(TransformedPositionField("Tangent 1", avgPoint.tangent));
|
|
avgPoint.SetTangent2Position(TransformedPositionField("Tangent 2", avgPoint.tangent2));
|
|
}
|
|
}
|
|
if (is2D)
|
|
{
|
|
avgPoint.SetPosition(TransformedPositionField2D("Position", avgPoint.position));
|
|
}
|
|
else
|
|
{
|
|
avgPoint.SetPosition(TransformedPositionField("Position", avgPoint.position));
|
|
}
|
|
|
|
if (!is2D)
|
|
{
|
|
avgPoint.normal = TransformedVectorField("Normal", avgPoint.normal);
|
|
}
|
|
avgPoint.size = EditorGUILayout.FloatField("Size", avgPoint.size);
|
|
avgPoint.color = EditorGUILayout.ColorField("Color", avgPoint.color);
|
|
if (isBezier)
|
|
{
|
|
avgPoint.type = (SplinePoint.Type)EditorGUILayout.EnumPopup("Point Type", avgPoint.type);
|
|
}
|
|
|
|
if (!EditorGUI.EndChangeCheck()) return;
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
points[selectedPoints[i]].SetPosition(GetChangedVector(avgPos, avgPoint.position, points[selectedPoints[i]].position));
|
|
points[selectedPoints[i]].normal = GetChangedVector(avgNormal, avgPoint.normal, points[selectedPoints[i]].normal);
|
|
|
|
if (isBezier)
|
|
{
|
|
points[selectedPoints[i]].SetTangentPosition(GetChangedVector(avgTan, avgPoint.tangent, points[selectedPoints[i]].tangent));
|
|
points[selectedPoints[i]].SetTangent2Position(GetChangedVector(avgTan2, avgPoint.tangent2, points[selectedPoints[i]].tangent2));
|
|
}
|
|
if (avgPoint.size != avgSize) points[selectedPoints[i]].size = avgPoint.size;
|
|
if (avgColor != avgPoint.color) points[selectedPoints[i]].color = avgPoint.color;
|
|
if (lastType != avgPoint.type) points[selectedPoints[i]].type = avgPoint.type;
|
|
}
|
|
ApplyModifiedProperties();
|
|
}
|
|
|
|
Vector3 GetChangedVector(Vector3 oldVector, Vector3 newVector, Vector3 original)
|
|
{
|
|
if (!Mathf.Approximately(oldVector.x, newVector.x)) original.x = newVector.x;
|
|
if (!Mathf.Approximately(oldVector.y, newVector.y)) original.y = newVector.y;
|
|
if (!Mathf.Approximately(oldVector.z, newVector.z)) original.z = newVector.z;
|
|
return original;
|
|
}
|
|
|
|
Vector3 TransformedPositionField(string title, Vector3 worldPoint)
|
|
{
|
|
Vector3 pos = worldPoint;
|
|
if (editSpace == Space.Local) pos = _matrix.inverse.MultiplyPoint3x4(worldPoint);
|
|
pos = EditorGUILayout.Vector3Field(title, pos);
|
|
if (editSpace == Space.Local) pos = _matrix.MultiplyPoint3x4(pos);
|
|
return pos;
|
|
}
|
|
|
|
Vector3 TransformedVectorField(string title, Vector3 worldPoint)
|
|
{
|
|
Vector3 vector = worldPoint;
|
|
if (editSpace == Space.Local) vector = _matrix.inverse.MultiplyVector(worldPoint);
|
|
vector = EditorGUILayout.Vector3Field(title, vector);
|
|
if (editSpace == Space.Local) vector = _matrix.MultiplyVector(vector);
|
|
return vector;
|
|
}
|
|
|
|
Vector2 TransformedPositionField2D(string title, Vector3 worldPoint)
|
|
{
|
|
Vector2 pos = worldPoint;
|
|
if (editSpace == Space.Local) pos = _matrix.inverse.MultiplyPoint3x4(worldPoint);
|
|
pos = EditorGUILayout.Vector2Field(title, pos);
|
|
if (editSpace == Space.Local) pos = _matrix.MultiplyPoint3x4(pos);
|
|
return pos;
|
|
}
|
|
|
|
public void FlatSelection(int axis)
|
|
{
|
|
Vector3 avg = Vector3.zero;
|
|
bool flatTangent = false;
|
|
bool flatPosition = true;
|
|
if (_typeProperty.enumValueIndex == (int)Spline.Type.Bezier)
|
|
{
|
|
switch (EditorUtility.DisplayDialogComplex("Flat Bezier", "How do you want to flat the selected Bezier points?", "Points Only", "Tangens Only", "Everything"))
|
|
{
|
|
case 0: flatTangent = false; flatPosition = true; break;
|
|
case 1: flatTangent = true; flatPosition = false; break;
|
|
case 2: flatTangent = true; flatPosition = true; break;
|
|
}
|
|
}
|
|
RecordUndo("Flat Selection");
|
|
if (flatPosition)
|
|
{
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
avg += points[selectedPoints[i]].position;
|
|
}
|
|
avg /= selectedPoints.Count;
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
Vector3 pos = points[selectedPoints[i]].position;
|
|
Vector3 nor = points[selectedPoints[i]].normal;
|
|
switch (axis)
|
|
{
|
|
case 0: pos.x = avg.x; nor.x = 0f; break;
|
|
case 1: pos.y = avg.y; nor.y = 0f; break;
|
|
case 2: pos.z = avg.z; nor.z = 0f; break;
|
|
}
|
|
points[selectedPoints[i]].normal = nor.normalized;
|
|
if (points[selectedPoints[i]].normal == Vector3.zero) points[selectedPoints[i]].normal = Vector3.up;
|
|
points[selectedPoints[i]].SetPosition(pos);
|
|
if (flatTangent)
|
|
{
|
|
Vector3 tan = points[selectedPoints[i]].tangent;
|
|
Vector3 tan2 = points[selectedPoints[i]].tangent2;
|
|
switch (axis)
|
|
{
|
|
case 0: tan.x = avg.x; tan2.x = avg.x; break;
|
|
case 1: tan.y = avg.y; tan2.y = avg.y; break;
|
|
case 2: tan.z = avg.z; tan2.z = avg.z; break;
|
|
}
|
|
points[selectedPoints[i]].SetTangentPosition(tan);
|
|
points[selectedPoints[i]].SetTangent2Position(tan2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
Vector3 tan = points[selectedPoints[i]].tangent;
|
|
Vector3 tan2 = points[selectedPoints[i]].tangent2;
|
|
Vector3 pos = points[selectedPoints[i]].position;
|
|
switch (axis)
|
|
{
|
|
case 0: tan.x = pos.x; tan2.x = pos.x; break;
|
|
case 1: tan.y = pos.y; tan2.y = pos.y; break;
|
|
case 2: tan.z = pos.z; tan2.z = pos.z; break;
|
|
}
|
|
points[selectedPoints[i]].SetTangentPosition(tan);
|
|
points[selectedPoints[i]].SetTangent2Position(tan2);
|
|
}
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
public void MirrorSelection(int axis)
|
|
{
|
|
bool mirrorTangents = false;
|
|
if (_typeProperty.enumValueIndex == (int)Spline.Type.Bezier)
|
|
{
|
|
if (EditorUtility.DisplayDialog("Mirror tangents", "Do you want to mirror the tangents too ?", "Yes", "No")) mirrorTangents = true;
|
|
}
|
|
float min = 0f, max = 0f;
|
|
switch (axis)
|
|
{
|
|
case 0: min = max = points[selectedPoints[0]].position.x; break;
|
|
case 1: min = max = points[selectedPoints[0]].position.y; break;
|
|
case 2: min = max = points[selectedPoints[0]].position.z; break;
|
|
}
|
|
RecordUndo("Mirror Selection");
|
|
if (mirrorTangents)
|
|
{
|
|
float value = 0f;
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[0]].tangent.x; break;
|
|
case 1: value = points[selectedPoints[0]].tangent.y; break;
|
|
case 2: value = points[selectedPoints[0]].tangent.z; break;
|
|
}
|
|
if (value < min) min = value;
|
|
if (value > max) max = value;
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[0]].tangent2.x; break;
|
|
case 1: value = points[selectedPoints[0]].tangent2.y; break;
|
|
case 2: value = points[selectedPoints[0]].tangent2.z; break;
|
|
}
|
|
if (value < min) min = value;
|
|
if (value > max) max = value;
|
|
}
|
|
for (int i = 1; i < selectedPoints.Count; i++)
|
|
{
|
|
float value = 0f;
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[i]].position.x; break;
|
|
case 1: value = points[selectedPoints[i]].position.y; break;
|
|
case 2: value = points[selectedPoints[i]].position.z; break;
|
|
}
|
|
if (value < min) min = value;
|
|
if (value > max) max = value;
|
|
if (mirrorTangents)
|
|
{
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[i]].tangent.x; break;
|
|
case 1: value = points[selectedPoints[i]].tangent.y; break;
|
|
case 2: value = points[selectedPoints[i]].tangent.z; break;
|
|
}
|
|
if (value < min) min = value;
|
|
if (value > max) max = value;
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[i]].tangent2.x; break;
|
|
case 1: value = points[selectedPoints[i]].tangent2.y; break;
|
|
case 2: value = points[selectedPoints[i]].tangent2.z; break;
|
|
}
|
|
if (value < min) min = value;
|
|
if (value > max) max = value;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
float value = 0f;
|
|
if (mirrorTangents)
|
|
{
|
|
//Point position
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[i]].position.x; break;
|
|
case 1: value = points[selectedPoints[i]].position.y; break;
|
|
case 2: value = points[selectedPoints[i]].position.z; break;
|
|
}
|
|
float percent = Mathf.InverseLerp(min, max, value);
|
|
value = Mathf.Lerp(max, min, percent);
|
|
Vector3 pos = points[selectedPoints[i]].position;
|
|
switch (axis)
|
|
{
|
|
case 0: pos.x = value; break;
|
|
case 1: pos.y = value; break;
|
|
case 2: pos.z = value; break;
|
|
}
|
|
points[selectedPoints[i]].position = pos;
|
|
//Tangent 1
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[i]].tangent.x; break;
|
|
case 1: value = points[selectedPoints[i]].tangent.y; break;
|
|
case 2: value = points[selectedPoints[i]].tangent.z; break;
|
|
}
|
|
percent = Mathf.InverseLerp(min, max, value);
|
|
value = Mathf.Lerp(max, min, percent);
|
|
Vector3 tan = points[selectedPoints[i]].tangent;
|
|
switch (axis)
|
|
{
|
|
case 0: tan.x = value; break;
|
|
case 1: tan.y = value; break;
|
|
case 2: tan.z = value; break;
|
|
}
|
|
points[selectedPoints[i]].tangent = tan;
|
|
//Tangent 2
|
|
switch (axis)
|
|
{
|
|
case 0: value = points[selectedPoints[i]].tangent2.x; break;
|
|
case 1: value = points[selectedPoints[i]].tangent2.y; break;
|
|
case 2: value = points[selectedPoints[i]].tangent2.z; break;
|
|
}
|
|
percent = Mathf.InverseLerp(min, max, value);
|
|
value = Mathf.Lerp(max, min, percent);
|
|
tan = points[selectedPoints[i]].tangent2;
|
|
switch (axis)
|
|
{
|
|
case 0: tan.x = value; break;
|
|
case 1: tan.y = value; break;
|
|
case 2: tan.z = value; break;
|
|
}
|
|
points[selectedPoints[i]].tangent2 = tan;
|
|
}
|
|
else
|
|
{
|
|
Vector3 pos = points[selectedPoints[i]].position;
|
|
switch (axis)
|
|
{
|
|
case 0: value = pos.x; break;
|
|
case 1: value = pos.y; break;
|
|
case 2: value = pos.z; break;
|
|
}
|
|
float percent = Mathf.InverseLerp(min, max, value);
|
|
value = Mathf.Lerp(max, min, percent);
|
|
switch (axis)
|
|
{
|
|
case 0: pos.x = value; break;
|
|
case 1: pos.y = value; break;
|
|
case 2: pos.z = value; break;
|
|
}
|
|
points[selectedPoints[i]].SetPosition(pos);
|
|
}
|
|
//Normal
|
|
Vector3 nor = points[selectedPoints[i]].normal;
|
|
switch (axis)
|
|
{
|
|
case 0: nor.x *= -1f; break;
|
|
case 1: nor.y *= -1f; break;
|
|
case 2: nor.z *= -1f; break;
|
|
}
|
|
points[selectedPoints[i]].normal = nor.normalized;
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
public void DistributeEvenly()
|
|
{
|
|
if (selectedPoints.Count < 3) return;
|
|
RecordUndo("Distribute Evenly");
|
|
int min = points.Length-1, max = 0;
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
if (selectedPoints[i] < min) min = selectedPoints[i];
|
|
if (selectedPoints[i] > max) max = selectedPoints[i];
|
|
}
|
|
double minPercent = (double)min / (points.Length - 1);
|
|
double maxPercent = (double)max / (points.Length - 1);
|
|
float length = calculateLength(minPercent, maxPercent);
|
|
float step = length / (max - min);
|
|
SplineSample evalResult = new SplineSample();
|
|
evaluate(minPercent, ref evalResult);
|
|
for (int i = min + 1; i < max; i++)
|
|
{
|
|
double percent = travel(evalResult.percent, step, Spline.Direction.Forward);
|
|
evaluate(percent, ref evalResult);
|
|
points[i].SetPosition(evalResult.position);
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
public void LoopTriggerProperties(System.Action<SerializedProperty> onTrigger)
|
|
{
|
|
SerializedProperty triggerGroups = serializedObject.FindProperty("triggerGroups");
|
|
|
|
for (int i = 0; i < triggerGroups.arraySize; i++)
|
|
{
|
|
SerializedProperty triggers = triggerGroups.GetArrayElementAtIndex(i).FindPropertyRelative("triggers");
|
|
for (int j = 0; j < triggers.arraySize; j++)
|
|
{
|
|
SerializedProperty trigger = triggers.GetArrayElementAtIndex(j);
|
|
onTrigger.Invoke(trigger);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AutoTangents()
|
|
{
|
|
RecordUndo("Auto Tangents");
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
int index = selectedPoints[i];
|
|
Vector3 prevPos = points[index].position, forwardPos = points[index].position;
|
|
if(index == 0 && points.Length > 1)
|
|
{
|
|
prevPos = points[0].position + (points[0].position - points[1].position);
|
|
} else prevPos = points[index - 1].position;
|
|
if (index == points.Length-1 && points.Length > 1)
|
|
{
|
|
forwardPos = points[points.Length-1].position + (points[points.Length - 1].position - points[points.Length - 2].position);
|
|
}
|
|
else forwardPos = points[index + 1].position;
|
|
Vector3 delta = (forwardPos - prevPos) / 2f;
|
|
points[index].tangent = points[index].position - delta / 3f;
|
|
points[index].tangent2 = points[index].position + delta / 3f;
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
public void SwapTangents()
|
|
{
|
|
RecordUndo("Swap Tangents");
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
int index = selectedPoints[i];
|
|
Vector3 tempTangent = points[index].tangent;
|
|
points[index].tangent = points[index].tangent2;
|
|
points[index].tangent2 = tempTangent;
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
public void FlipTangents()
|
|
{
|
|
RecordUndo("Flip Tangents");
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
int index = selectedPoints[i];
|
|
points[index].tangent = points[index].position + (points[index].position - points[index].tangent);
|
|
points[index].tangent2 = points[index].position + (points[index].position - points[index].tangent2);
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
public void FlipFirstTangent()
|
|
{
|
|
RecordUndo("Flip First Tangent");
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
int index = selectedPoints[i];
|
|
points[index].tangent2 = points[index].position + (points[index].position - points[index].tangent2);
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
public void FlipSecondTangent()
|
|
{
|
|
RecordUndo("Flip Second Tangent");
|
|
for (int i = 0; i < selectedPoints.Count; i++)
|
|
{
|
|
int index = selectedPoints[i];
|
|
points[index].tangent = points[index].position + (points[index].position - points[index].tangent);
|
|
}
|
|
ResetCurrentModule();
|
|
}
|
|
|
|
protected void ResetCurrentModule()
|
|
{
|
|
if (_module < 0 || _module >= _modules.Length) return;
|
|
_modules[_module].Reset();
|
|
}
|
|
|
|
public class PointOperation
|
|
{
|
|
public string name = "";
|
|
public System.Action action;
|
|
}
|
|
}
|
|
}
|