rj-action-library/Runtime/Math/Geometry/SplineCurveCreator.cs

187 lines
5.8 KiB
C#

using Godot;
using System.Collections.Generic;
using System;
namespace Rokojori
{
public class SplineCurveCreator
{
bool closed;
public SplineCurve Create( List<SplinePoint> splinePoints, bool close )
{
closed = close;
var points = new List<SplineCurvePoint>();
for ( int i = 0; i < splinePoints.Count; i++ )
{
points.Add( CreatePoint( splinePoints, i ) );
}
if ( closed )
{
points.Add( CreatePoint( splinePoints, 0 ) );
}
return SplineCurve.From( points );
}
SplineCurvePoint CreatePoint( List<SplinePoint> splinePoints, int index )
{
var splineCurvePoint = new SplineCurvePoint();
var splinePoint = splinePoints[ index ];
splineCurvePoint.position = splinePoint.GlobalPosition;
splineCurvePoint.rotation = splinePoint.GetGlobalQuaternion();
splineCurvePoint.scale = splinePoint.Scale;
splineCurvePoint.twist = splinePoint.twist;
splineCurvePoint.weight = 1f;
splineCurvePoint.tangentBefore.position = GetTangentPosition( splinePoints, index, true, closed );
splineCurvePoint.tangentNext.position = GetTangentPosition( splinePoints, index, false, closed );
splineCurvePoint.tangentBefore.weight = splinePoint.tangentBeforeWeight;
splineCurvePoint.tangentNext.weight = splinePoint.tangentNextWeight;
return splineCurvePoint;
}
public static Vector3 GetTangentDirectionSmoothed( float smoothing, List<SplinePoint> splinePoints, int index, bool before, bool closed )
{
var previousIndex = MathX.SafeIndex( index - 1, splinePoints.Count, closed );
var currentIndex = index;
var nextIndex = MathX.SafeIndex( index + 1, splinePoints.Count, closed );
var previousDirection = GetTangentDirection( splinePoints, previousIndex, before, closed );
var currentDirection = GetTangentDirection( splinePoints, currentIndex, before, closed );
var nextDirection = GetTangentDirection( splinePoints, nextIndex, before, closed );
var smoothed = ( previousDirection + currentDirection + nextDirection ) / 3.0f;
return currentDirection + smoothing * ( smoothed - currentDirection );
}
public static Vector3 GetTangentDirectionSmoothed( int numSamples, float smoothing, List<SplinePoint> splinePoints, int index, bool before, bool closed )
{
var smoothedTangent = Vector3.Zero;
var unsmoothedTangent = Vector3.Zero;
for ( int i = -numSamples; i <= numSamples; i++ )
{
var sampleIndex = MathX.SafeIndex( index + i, splinePoints.Count, closed );
var direction = GetTangentDirection( splinePoints, sampleIndex, before, closed );
smoothedTangent += direction;
if ( i == 0 )
{
unsmoothedTangent = direction;
}
}
smoothedTangent /= ( numSamples * 2 + 1 );
return unsmoothedTangent + smoothing * ( smoothedTangent - unsmoothedTangent );
}
public static Vector3 GetTangentDirection( List<SplinePoint> splinePoints, int index, bool before, bool closed )
{
var splinePoint = splinePoints[ index ];
return GetTangentPosition( splinePoints, index, before, closed ) - splinePoint.GlobalPosition;
}
public static Vector3 GetTangentPosition( List<SplinePoint> splinePoints, int index, bool before, bool closed )
{
var splinePoint = splinePoints[ index ];
if ( splinePoint.tangentMode == SplinePointTangentMode.Custom )
{
return before ? splinePoint.tangentBefore.GlobalPosition :
splinePoint.tangentNext.GlobalPosition;
}
var previousIndex = index - 1;
if ( previousIndex == -1 )
{
previousIndex = closed ? splinePoints.Count -1 : 0;
}
var nextIndex = index + 1;
if ( nextIndex == splinePoints.Count )
{
nextIndex = closed ? 0 : splinePoints.Count - 1;
}
var previous = splinePoints[ previousIndex ];
var next = splinePoints[ nextIndex ];
var previousPosition = previous.GlobalPosition;
var nextPosition = next.GlobalPosition;
var point = splinePoint.GlobalPosition;
var overshootPrevention = splinePoint.overshootPrevention;
var tangentScale = splinePoint.tangentScale;
var symmetricTangentLength = splinePoint.symmetricTangentLength;
if ( overshootPrevention > 0 )
{
var previousDirection = ( previousPosition - point ) ;
var nextDirection = ( nextPosition - point );
var previousLength = previousDirection.Length();
var nextLength = nextDirection.Length();
if ( previousLength > nextLength )
{
previousPosition = Math3D.Lerp(
previousPosition, point + previousDirection.Normalized() * nextLength,
overshootPrevention
);
}
else
{
nextPosition = Math3D.Lerp(
nextPosition, point + nextDirection.Normalized() * previousLength,
overshootPrevention
);
}
}
var direction = nextPosition - previousPosition;
var length = 0f;
var lengthBefore = ( point - previousPosition ).Length();
var lengthNext = ( point - nextPosition ).Length();
var lengthAverage = ( lengthBefore + lengthNext ) * 0.5f;
if ( symmetricTangentLength > 0 )
{
lengthBefore = Mathf.Lerp( lengthBefore, lengthAverage, symmetricTangentLength );
lengthNext = Mathf.Lerp( lengthNext, lengthAverage, symmetricTangentLength );
}
if ( before )
{
length = -lengthBefore;
}
else
{
length = lengthNext;
}
return point + direction.Normalized() * length * 0.33333f * tangentScale;
}
}
}