rj-action-library/Runtime/Procedural/Parametric/Spline/Spline.cs

293 lines
6.0 KiB
C#

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<SplinePoint>( 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<SplinePoint>( 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<Vector3>();
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<Vector3> cachedPositions = new List<Vector3>();
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<SplinePoint>( 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<SplinePoint,Vector3>( this, sp => sp.GlobalPosition );
// RJLog.Log( "Updating gizmos" );
UpdateGizmos();
}
#endif
void AutoOrientate()
{
var list = Nodes.GetDirectChildren<SplinePoint>( 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 );
}
}
}
}
}