using Godot; using Rokojori; using System.Collections.Generic; namespace Rokojori { public enum SplineAutoOrientationMode { Tangent, Next_Neighbor } [Tool] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Spline.svg") ] public partial class Spline : Node3D #if TOOLS , GizmoDrawer #endif { [Export] public bool closed = false; [Export] public int editorResolution = 20; [Export] public bool autoOrienation = false; [Export] public SplineAutoOrientationMode autoOrientationMode = SplineAutoOrientationMode.Tangent; [Export( PropertyHint.Range, "-1,1")] public float autoOrientationTangentAdjustment = 0f; [Export] public Vector3 autoOrientationUp = Vector3.Up; [Export] public bool updateAlways = false; [Export] public int xzPathBakeResolution = 50; SplineCurve splineCurve; Vector3 min; Vector3 max; public Box3 GetBounds() { GetCurve(); return Box3.Create( min, max ); } public SplineCurve GetCurve() { if ( splineCurve == null ) { var splinePoints = Nodes.GetDirectChildren( this ); splineCurve = new SplineCurveCreator().Create( splinePoints, closed ); min = splineCurve.MinPointPosition(); max = splineCurve.MaxPointPosition(); } return splineCurve; } SplineCurve splineCurveXZ; public SplineCurve GetCurveXZ() { if ( splineCurveXZ == null ) { var splinePoints = Nodes.GetDirectChildren( this ); splineCurveXZ = new SplineCurveCreator().Create( splinePoints, closed ).CloneForXZ( 0 ); } return splineCurveXZ; } Path2 xzPath; Convex2Group convex2Group; public Path2 GetXZPath2() { if ( xzPath == null ) { xzPath = Path2.AsXZFrom( GetCurve(), closed, xzPathBakeResolution ); } return xzPath; } public Convex2Group GetConvex2Group() { if ( convex2Group == null ) { convex2Group = Convex2Group.FromPath( GetXZPath2() ); } return convex2Group; } public void ClearCurveCache() { splineCurve = null; splineCurveXZ = null; xzPath = null; convex2Group = null; } #if TOOLS public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo ) { ClearCurveCache(); var curve = GetCurve(); gizmo.Clear(); if ( curve.points.Count <= 1 ) { return; } var linePoints = new List(); int resolution = editorResolution <= 0 ? 20 : editorResolution; renderedResolution = editorResolution; var lastPoint = ToLocal( curve.PositionAt( 0 ) ); for ( int i = 1; i < resolution; i++ ) { var t = i / (float) (resolution - 1 ); var point = ToLocal( curve.PositionAt( t ) ); linePoints.Add( lastPoint ); linePoints.Add( point ); lastPoint = point; } for ( int i = 0; i < curve.points.Count; i++ ) { var p = curve.points[ i ]; linePoints.Add( ToLocal( p.position ) ); linePoints.Add( ToLocal( p.tangentBefore.position ) ); linePoints.Add( ToLocal( p.position ) ); linePoints.Add( ToLocal( p.tangentNext.position ) ); } var material = gizmoPlugin.GetMaterial( "main", gizmo ); gizmo.AddLines( linePoints.ToArray(), material, false ); } List cachedPositions = new List(); int renderedResolution = -100; public override void _Process( double delta ) { var changed = updateAlways; var index = 0; if ( renderedResolution != editorResolution ) { changed = true; } if ( autoOrienation ) { AutoOrientate(); } Nodes.ForEachDirectChild( this, sp => { if ( changed ) { return; } if ( index >= cachedPositions.Count ) { changed = true; return; } if ( cachedPositions[ index ] != sp.GlobalPosition ) { changed = true; } index ++; } ); if ( ! changed ) { return; } cachedPositions = Nodes.MapDirectChildren( this, sp => sp.GlobalPosition ); // RJLog.Log( "Updating gizmos" ); UpdateGizmos(); } #endif void AutoOrientate() { var list = Nodes.GetDirectChildren( this ); if ( list.Count <= 1 ) { return; } if ( SplineAutoOrientationMode.Next_Neighbor == autoOrientationMode ) { for ( int i = 0; i < list.Count; i++ ) { var sp = list[ i ]; if ( i == ( list.Count - 1 ) ) { if ( closed ) { var first = list[ 0 ]; sp.LookAt( first.GlobalPosition, autoOrientationUp ); } continue; } var next = list[ i + 1 ]; sp.LookAt( next.GlobalPosition, autoOrientationUp ); } } else if ( SplineAutoOrientationMode.Tangent == autoOrientationMode ) { for ( int i = 0; i < list.Count; i++ ) { var sp = list[ i ]; var tangentForward = Vector3.Zero; if ( i == ( list.Count - 1 ) && ! closed ) { tangentForward = - SplineCurveCreator.GetTangentDirectionSmoothed( 1, autoOrientationTangentAdjustment, list, i, true, false ); } else { tangentForward = SplineCurveCreator.GetTangentDirectionSmoothed( 1, autoOrientationTangentAdjustment, list, i, false, closed ); } if ( tangentForward.Length() == 0 ) { continue; } sp.LookAt( sp.GlobalPosition + tangentForward, autoOrientationUp ); } } } } }