/// <summary> /// generates an arc from start to end with the arc axis perpendicular to start and end points /// </summary> /// <returns>The arc.</returns> /// <param name="start">Start.</param> /// <param name="end">End.</param> /// <param name="curvature">how far away from the line from start to end the arc extends</param> public static Spline GenerateArc(Vector3 start, Vector3 end, float curvature) { return(Spline.GenerateArc(start, end, curvature, Vector3.Cross(start, end))); }
/// <summary> /// generates an arc from start to end with the arc axis curvatureAxis /// </summary> /// <returns>The arc.</returns> /// <param name="start">Start.</param> /// <param name="end">End.</param> /// <param name="curvature">how far away from the line from start to end the arc extends</param> /// <param name="curvatureAxis">the axis which the arc should extend into</param> public static Spline GenerateArc(Vector3 start, Vector3 end, float curvature, Vector3 curvatureAxis) { curvatureAxis.Normalize(); return(Spline.GenerateArc(start, end, curvature, curvatureAxis, curvatureAxis)); }
public override void OnInspectorGUI() { showEditModeToggle(); if (!_target.IsInEditMode) { DrawInstructions(); return; } if (_target.UseBezier && _target.Nodes.Count < 4) { _target.UseBezier = false; } // what kind of handles shall we use? EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Use Standard Handles"); _target.UseStandardHandles = EditorGUILayout.Toggle(_target.UseStandardHandles); EditorGUILayout.EndHorizontal(); // path name: EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Route Name"); _target.PathName = EditorGUILayout.TextField(_target.PathName); EditorGUILayout.EndHorizontal(); if (_target.PathName == string.Empty) { _target.PathName = "route" + Random.Range(1, 100000); } // path color: EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Route Color"); _target.PathColor = EditorGUILayout.ColorField(_target.PathColor); EditorGUILayout.EndHorizontal(); // force bezier: GUI.enabled = !_target.ForceStraightLinePath; EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Use Bezier Path"); EditorGUI.BeginChangeCheck(); _target.UseBezier = EditorGUILayout.Toggle(_target.UseBezier); if (EditorGUI.EndChangeCheck() && _target.UseBezier) { // validate // make sure we have enough nodes to use a bezier. nodeCount - 1 must be divisible by 3 if (_target.Nodes.Count < 4) { Debug.LogError("there must be at least 4 nodes to use a bezier"); _target.UseBezier = false; } else { var excessNodes = (_target.Nodes.Count - 1) % 3; if (excessNodes > 0) { _target.Nodes.RemoveRange(_target.Nodes.Count - excessNodes, excessNodes); Debug.LogWarning("trimming " + excessNodes + " from the node list to make a proper bezier spline"); } } } EditorGUILayout.EndHorizontal(); GUI.enabled = true; // force straight lines: GUI.enabled = !_target.UseBezier; EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Force Straight Line Path"); _target.ForceStraightLinePath = EditorGUILayout.Toggle(_target.ForceStraightLinePath); EditorGUILayout.EndHorizontal(); GUI.enabled = true; // close path. only relevant for node counts greater than 5 if (_target.Nodes.Count < 5) { GUI.enabled = false; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Close Path"); EditorGUI.BeginChangeCheck(); _target.ClosePath = EditorGUILayout.Toggle(_target.ClosePath); if (EditorGUI.EndChangeCheck()) { if (_target.ClosePath) { closeRoute(); } } EditorGUILayout.EndHorizontal(); GUI.enabled = true; // resolution EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Editor Drawing Resolution"); _target.PathResolution = EditorGUILayout.IntSlider(_target.PathResolution, 2, 100); EditorGUILayout.EndHorizontal(); EditorGUILayout.Separator(); // insert node - we need 3 or more nodes for insert to make sense if (_target.Nodes.Count > 2) { EditorGUILayout.BeginHorizontal(); GUI.enabled = _selectedNodeIndex != 0 && _selectedNodeIndex != -1; if (GUILayout.Button("Insert Node Before Selected")) { insertNodeAtIndex(_selectedNodeIndex, false); } GUI.enabled = _selectedNodeIndex != _target.Nodes.Count - 1 && _selectedNodeIndex != -1; if (GUILayout.Button("Insert Node After Selected")) { insertNodeAtIndex(_selectedNodeIndex, true); } GUI.enabled = true; EditorGUILayout.EndHorizontal(); } // shift the start point to the origin if (GUILayout.Button("Shift Path to Start at Origin")) { Undo.RecordObject(_target, "Path Vector Changed"); var offset = Vector3.zero; // see what kind of path we are. the simplest case is just a straight line var path = new Spline(_target.Nodes, _target.UseBezier, _target.ForceStraightLinePath); if (path.SplineType == SplineType.StraightLine || path.SplineType == SplineType.Bezier || _target.Nodes.Count < 5) { offset = Vector3.zero - _target.Nodes[0]; } else { offset = Vector3.zero - _target.Nodes[1]; } for (var i = 0; i < _target.Nodes.Count; i++) { _target.Nodes[i] += offset; } GUI.changed = true; } // reverse if (GUILayout.Button("Reverse Path")) { Undo.RecordObject(_target, "Path Vector Changed"); _target.Nodes.Reverse(); GUI.changed = true; } // shifters. thse only make sense for straight line and catmull rom if (_target.ForceStraightLinePath || (_target.Nodes.Count > 4 && !_target.UseBezier)) { GUILayout.BeginHorizontal(); if (GUILayout.Button("Shift Nodes Left")) { Undo.RecordObject(_target, "Path Vector Changed"); var firstItem = _target.Nodes[0]; _target.Nodes.RemoveAt(0); _target.Nodes.Add(firstItem); GUI.changed = true; } if (GUILayout.Button("Shift Nodes Right")) { Undo.RecordObject(_target, "Path Vector Changed"); var lastItem = _target.Nodes[_target.Nodes.Count - 1]; _target.Nodes.RemoveAt(_target.Nodes.Count - 1); _target.Nodes.Insert(0, lastItem); GUI.changed = true; } GUILayout.EndHorizontal(); } if (GUILayout.Button("Clear Path")) { Undo.RecordObject(_target, "Path Vector Changed"); _target.Nodes.Clear(); _target.Nodes.Add(_target.transform.position); _target.Nodes.Add(_target.transform.position + new Vector3(5f, 5f)); GUI.changed = true; } if (GUILayout.Button("Move z-axis Values to 0")) { Undo.RecordObject(_target, "Path Vector Changed"); for (var i = 0; i < _target.Nodes.Count; i++) { _target.Nodes[i] = new Vector3(_target.Nodes[i].x, _target.Nodes[i].y, 0f); } GUI.changed = true; } // persist to disk EditorGUILayout.Space(); EditorGUILayout.LabelField("Save to/Read from Disk"); // Should we save it as a ScriptableObject instead? EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Save As Scriptable Object"); _saveAsScriptableObject = EditorGUILayout.Toggle(_saveAsScriptableObject); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Serialize and Save Path"); if (GUILayout.Button("Save")) { if (_saveAsScriptableObject) { if (_saveAsScriptableObject) { var folderPath = EditorUtility.OpenFolderPanel("Save Folder", Application.dataPath, ""); if (folderPath != string.Empty) { saveAsScriptableObject(folderPath.Replace(Application.dataPath, "Assets") + "/" + _target.PathName + ".asset"); } } } else { var path = EditorUtility.SaveFilePanel("Save path", Application.dataPath + "/StreamingAssets", _target.PathName + ".asset", "asset"); if (path != string.Empty) { persistRouteToDisk(path); // fetch the filename and set it as the routeName _target.PathName = Path.GetFileName(path).Replace(".asset", string.Empty); } } GUI.changed = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Load From Scriptable Object"); _loadFromScriptableObject = EditorGUILayout.Toggle(_loadFromScriptableObject); EditorGUILayout.EndHorizontal(); // load from disk EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Load saved path"); if (GUILayout.Button("Load")) { if (_loadFromScriptableObject) { var path = EditorUtility.OpenFilePanel("Choose path to load", Path.Combine(Application.dataPath, "Asset"), "asset"); if (path != string.Empty) { MrTweenSplineSettings asset = AssetDatabase.LoadAssetAtPath(path.Replace(Application.dataPath, "Assets"), typeof(MrTweenSplineSettings)) as MrTweenSplineSettings; if (asset != null) { _target.Nodes = asset.Nodes; _target.PathName = Path.GetFileName(path).Replace(".asset", string.Empty); } else { EditorUtility.DisplayDialog("Incorrect file selected", "File selected does not contain a valid path", "Close"); } } } else { var path = EditorUtility.OpenFilePanel("Choose path to load", Path.Combine(Application.dataPath, "StreamingAssets"), "asset"); if (path != string.Empty) { if (!File.Exists(path)) { EditorUtility.DisplayDialog("File does not exist", "Path couldn't find the file you specified", "Close"); } else { _target.Nodes = SplineAssetUtils.BytesToVector3List(File.ReadAllBytes(path)); _target.PathName = Path.GetFileName(path).Replace(".asset", string.Empty); } } GUI.changed = true; } } EditorGUILayout.EndHorizontal(); // node display EditorGUILayout.Space(); _showNodeDetails = EditorGUILayout.Foldout(_showNodeDetails, "Show Node Values"); if (_showNodeDetails) { EditorGUI.indentLevel++; for (int i = 0; i < _target.Nodes.Count; i++) { _target.Nodes[i] = EditorGUILayout.Vector3Field("Node " + (i + 1), _target.Nodes[i]); } EditorGUI.indentLevel--; } DrawInstructions(); // update and redraw: if (GUI.changed) { EditorUtility.SetDirty(_target); Repaint(); } }