public void OnDrawGizmosSelected() { // the editor will draw paths when force straight line is on if( !forceStraightLinePath ) { var spline = new Spline( nodes, useBezier, forceStraightLinePath ); if( closePath ) spline.closePath(); Gizmos.color = pathColor; spline.drawGizmos( pathResolution, isInEditMode ); } }
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"); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Serialize and Save Path"); if (GUILayout.Button("Save")) { 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(); // load from disk EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Load saved path"); if (GUILayout.Button("Load")) { 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(); } }
/// <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)); }
/// <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> /// helper for drawing gizmos in the editor /// </summary> public static void drawGizmos( Vector3[] nodes, float resolution = 50, bool isInEditMode = false ) { // horribly inefficient but it only runs in the editor var spline = new Spline( new List<Vector3>( nodes ) ); spline.drawGizmos( resolution, isInEditMode ); }
private void closeRoute() { // we will use the GoSpline class to handle the dirtywork of closing the path var path = new Spline( _target.nodes, _target.useBezier, _target.forceStraightLinePath ); path.closePath(); _target.nodes = path.nodes; GUI.changed = true; }
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" ); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel( "Serialize and Save Path" ); if( GUILayout.Button( "Save" ) ) { 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(); // load from disk EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel( "Load saved path" ); if( GUILayout.Button( "Load" ) ) { 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(); } }