using Godot; using System.Collections.Generic; using System; namespace Rokojori { public class SplineCurveCreator { bool closed; public SplineCurve Create( List splinePoints, bool close ) { closed = close; var points = new List(); 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 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 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 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 splinePoints, int index, bool before, bool closed ) { var splinePoint = splinePoints[ index ]; return GetTangentPosition( splinePoints, index, before, closed ) - splinePoint.GlobalPosition; } public static Vector3 GetTangentPosition( List 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; } } }