Editor/PathTool.cs
using Editor.MeshEditor;
using Editor.ShaderGraph;
namespace PathTool.Editor;
/// Create a new path
/// Left Click = Add Node
/// Escape = Delete Node
/// Enter = Create Path
[EditorTool]
[Title( "Path" )]
[Icon( "route" )]
[Group( "9" )]
[Order(10000)]
public partial class PathTool : EditorTool
{
public bool InProgress => nodes == null || nodes.Count <= 1;
Vector3 origin;
Type pathType;
Type nodeType;
Path path;
List<PathNode> nodes => path?.Nodes;
PathNode selectedNode;
bool hasDeleted = false;
public PathTool()
{
AllowGameObjectSelection = false;
}
public override void OnEnabled()
{
base.OnEnabled();
if(pathType == null || !pathType.IsAssignableTo( typeof( Path ) ) )
{
SelectPathType( EditorTypeLibrary.GetType<Path>() );
}
AllowGameObjectSelection = false;
Selection.Clear();
Selection.Set( this );
}
public override void OnUpdate()
{
const float NODE_RADIUS = 2.5f;
const float ARROW_WIDTH = 1.5f;
const float ARROW_LENGTH = 4f;
float textSize = 22 * Gizmo.Settings.GizmoScale * Application.DpiScale;
bool nodeSelected = false;
if ( nodes.Count > 0 )
{
using(Gizmo.Scope("Path", origin))
{
for ( int i = 0; i < nodes.Count; i++ )
{
var node = nodes[i];
Vector3 pos = node.Position;
using(Gizmo.Scope($"Node{i}", pos ) )
{
Gizmo.Draw.Color = Color.White;
if(i < nodes.Count - 1)
{
Gizmo.Draw.Arrow( Vector3.Zero, nodes[i + 1].Position - pos, ARROW_LENGTH, ARROW_WIDTH );
}
if(!nodeSelected && Gizmo.Pressed.This)
{
SelectNode( node );
}
if (node == selectedNode)
{
Gizmo.Draw.Color = Color.Green;
if(Gizmo.Control.Position( "NodePosition", Vector3.Zero, out Vector3 delta ))
{
node.Position += Gizmo.Snap(delta, delta);
}
}
Gizmo.Draw.ScreenText( $"{i + 1}", Gizmo.Camera.ToScreen( pos + origin ) + Vector2.Up * NODE_RADIUS * 3, size: textSize );
Gizmo.Draw.SolidSphere( Vector3.Zero, NODE_RADIUS );
Gizmo.Hitbox.Sphere( new Sphere( Vector3.Zero, NODE_RADIUS ) );
}
}
Gizmo.Draw.Color = Color.Yellow;
Gizmo.Draw.Arrow( nodes[nodes.Count - 1].Position, nodes[0].Position, ARROW_LENGTH, ARROW_WIDTH );
}
}
if ( !Gizmo.Pressed.Any && Gizmo.WasLeftMousePressed )
{
CreateNode();
}
if ( Application.FocusWidget is not null )
{
if ( Application.IsKeyDown( KeyCode.Enter ) )
{
var obj = CreatePath();
Selection.Clear();
Selection.Add( obj );
EditorToolManager.CurrentModeName = null;
}
else if(!hasDeleted)
{
if ( Application.IsKeyDown( KeyCode.Escape ) )
{
hasDeleted = true;
RemoveLastNode();
}
else if(selectedNode != null && Application.IsKeyDown(KeyCode.Backspace))
{
hasDeleted = true;
RemoveNode( selectedNode );
}
}
if ( !Application.IsKeyDown( KeyCode.Escape ) )
{
hasDeleted = false;
}
}
}
public override void OnSelectionChanged()
{
base.OnSelectionChanged();
if ( !Selection.OfType<GameObject>().Any() )
{
Selection.Set( this );
return;
}
EditorToolManager.CurrentModeName = "object";
//_finished = true;
}
private void CreateNode()
{
var tr = Trace.UseRenderMeshes( true )
.UsePhysicsWorld( true )
.Run();
if ( !tr.Hit ) return;
Vector3 position = tr.EndPosition;
if ( Gizmo.Settings.SnapToGrid != Gizmo.IsCtrlPressed )
{
position = position.SnapToGrid( Gizmo.Settings.GridSpacing );
}
if(nodes.Count == 0)
{
origin = position;
position = Vector3.Zero;
}
else
{
position = position - origin;
}
PathNode node = EditorTypeLibrary.Create<PathNode>( nodeType );
node.Position = position;
nodes.Add( node );
SelectNode( node );
UpdateStatus();
}
private void SelectNode(PathNode node)
{
//Log.Info( node );
selectedNode = node;
BuildControlSheet();
}
private void RemoveLastNode()
{
if ( nodes.Count > 0 )
{
RemoveNode( nodes[nodes.Count - 1] );
}
}
public void RemoveNode(PathNode node)
{
nodes.Remove( node );
Selection.Clear();
Selection.Set( this );
UpdateStatus();
}
private GameObject CreatePath()
{
GameObject obj = Scene.CreateObject();
obj.Name = "Path";
obj.MakeNameUnique();
obj.Transform.Position = origin;
var comp = obj.Components.Create(EditorTypeLibrary.GetType(pathType));
try
{
comp.GetSerialized().GetProperty( "Nodes" ).SetValue( nodes );
}
catch(Exception e)
{
Log.Info($"Error while serializing: {e.Message}");
}
return obj;
}
private IEnumerable<TypeDescription> GetPathTypes()
{
return EditorTypeLibrary.GetTypes<Path>();
}
}