using UnityEngine; using System; using QuaternionUtilities; using System.Collections.Generic; public partial class Spline : MonoBehaviour { SplineInterpolator splineInterpolator = new HermiteInterpolator( ); ///< The SplineInterpolator that will be used for spline interpolation. } /// /// The SplineInterpolator class provides functions that are necessary to interpolate positions, rotations, values, etc. of and on splines. /// /// /// It can be used to manually interpolate an array of vectors/quaternions/values without having to instanciate a whole new spline. /// Depending on its settings it provides different interpolation modes. /// /// Please note that this class doesn't provide constant-velocity interpolation, when used on its own. If you need constant-velocity interpolation /// use the regular Spline class! /// [Serializable] public class SplineInterpolator { protected double[] coefficientMatrix; protected int[] nodeIndices; protected SplineInterpolator( ) { } /// /// Initializes a new instance of the class. /// /// /// The coefficient 4x4 matrix that will be used for the spline interpolation. /// The array must contain exactly 16 elements! /// /// /// The relative indices of the nodes that are needed for the interpolation. /// The array must contain exactly 4 elements! /// public SplineInterpolator( double[] coefficientMatrix, int[] nodeIndices ) { CheckMatrix( coefficientMatrix ); CheckIndices( nodeIndices ); this.coefficientMatrix = coefficientMatrix; this.nodeIndices = nodeIndices; } public double[] CoefficientMatrix{ get{ return coefficientMatrix; } set{ CheckMatrix( value ); coefficientMatrix = value; } } ///< Returns or sets the coefficients matrix for custom interpolation public int[] NodeIndices{ get{ return nodeIndices; } set{ CheckIndices( value ); nodeIndices = value; } } ///< Returns or sets the coefficients matrix for custom interpolation public Matrix4x4 CoefficientMatrix4x4{ get{ Matrix4x4 matrix = new Matrix4x4( ); matrix[ 0] = (float)coefficientMatrix[ 0]; matrix[ 1] = (float)coefficientMatrix[ 1]; matrix[ 2] = (float)coefficientMatrix[ 2]; matrix[ 3] = (float)coefficientMatrix[ 3]; matrix[ 4] = (float)coefficientMatrix[ 4]; matrix[ 5] = (float)coefficientMatrix[ 5]; matrix[ 6] = (float)coefficientMatrix[ 6]; matrix[ 7] = (float)coefficientMatrix[ 7]; matrix[ 8] = (float)coefficientMatrix[ 8]; matrix[ 9] = (float)coefficientMatrix[ 9]; matrix[10] = (float)coefficientMatrix[10]; matrix[11] = (float)coefficientMatrix[11]; matrix[12] = (float)coefficientMatrix[12]; matrix[13] = (float)coefficientMatrix[13]; matrix[14] = (float)coefficientMatrix[14]; matrix[15] = (float)coefficientMatrix[15]; return matrix; } set{ coefficientMatrix[ 0] = value[ 0]; coefficientMatrix[ 1] = value[ 1]; coefficientMatrix[ 2] = value[ 2]; coefficientMatrix[ 3] = value[ 3]; coefficientMatrix[ 4] = value[ 4]; coefficientMatrix[ 5] = value[ 5]; coefficientMatrix[ 6] = value[ 6]; coefficientMatrix[ 7] = value[ 7]; coefficientMatrix[ 8] = value[ 8]; coefficientMatrix[ 9] = value[ 9]; coefficientMatrix[10] = value[10]; coefficientMatrix[11] = value[11]; coefficientMatrix[12] = value[12]; coefficientMatrix[13] = value[13]; coefficientMatrix[14] = value[14]; coefficientMatrix[15] = value[15]; } } ///< Returns or sets the coefficients matrix for custom interpolation /// /// Interpolates an array of vectors. /// /// /// The interpolated vector. /// /// /// An interpolation parameter from 0 to 1. /// /// /// The index of the current spline segment. /// /// /// If enabled the vector array will be treated as closed spline. /// /// /// An array of vectors. /// /// /// The derivation order. /// public virtual Vector3 InterpolateVector( double t, int index, bool autoClose, IList nodes, int derivationOrder ) { Vector3 v0; Vector3 v1; Vector3 v2; Vector3 v3; GetNodeData( nodes, index, autoClose, out v0, out v1, out v2, out v3 ); return InterpolateVector( t, v0, v1, v2, v3, derivationOrder ); } /// /// Interpolates the positions of an array of SplineNodes. /// /// /// The interpolated position. /// /// /// An interpolation parameter from 0 to 1. /// /// /// The index of the current spline segment. /// /// /// If enabled the node array will be treated as closed spline. /// /// /// An array of SplineNodes. /// /// /// The derivation order. /// public virtual Vector3 InterpolateVector( Spline spline, double t, int index, bool autoClose, IList nodes, int derivationOrder ) { SplineNode n0; SplineNode n1; SplineNode n2; SplineNode n3; GetNodeData( nodes, index, autoClose, out n0, out n1, out n2, out n3 ); return InterpolateVector( t, n0.Position, n1.Position, n2.Position, n3.Position, derivationOrder ); } /// /// Interpolates an array of values. /// /// /// The interpolated value /// /// /// An interpolation parameter from 0 to 1. /// /// /// The index of the current spline segment. /// /// /// If enabled the value array will be treated as closed spline. /// /// /// An array of float values /// /// /// Derivation order. /// public virtual float InterpolateValue( double t, int index, bool autoClose, IList nodes, int derivationOrder ) { float v0; float v1; float v2; float v3; GetNodeData( nodes, index, autoClose, out v0, out v1, out v2, out v3 ); return InterpolateValue( t, v0, v1, v2, v3, derivationOrder ); } /// /// Interpolates the custom values of an array of SplineNodes. /// /// /// The interpolated value. /// /// /// An interpolation parameter from 0 to 1. /// /// /// The index of the current spline segment. /// /// /// If enabled the node array will be treated as closed spline. /// /// /// An array of SplineNodes /// /// /// Derivation order. /// public virtual float InterpolateValue( Spline spline, double t, int index, bool autoClose, IList nodes, int derivationOrder ) { SplineNode n0; SplineNode n1; SplineNode n2; SplineNode n3; GetNodeData( nodes, index, autoClose, out n0, out n1, out n2, out n3 ); return InterpolateValue( t, n0.CustomValue, n1.CustomValue, n2.CustomValue, n3.CustomValue, derivationOrder ); } /// /// Interpolates an array of rotations. /// /// /// The interpolated rotation. /// /// /// An interpolation parameter from 0 to 1. /// /// /// The index of the current spline segment. /// /// /// If enabled the quaternion array will be treated as closed spline. /// /// /// An array of rotations. /// /// /// Derivation order. /// public virtual Quaternion InterpolateRotation( double t, int index, bool autoClose, IList nodes, int derivationOrder ) { Quaternion q0; Quaternion q1; Quaternion q2; Quaternion q3; GetNodeData( nodes, index, autoClose, out q0, out q1, out q2, out q3 ); return InterpolateRotation( t, q0, q1, q2, q3, derivationOrder ); } /// /// Interpolates the rotations of an array of SplineNodes. /// /// /// The interpolated rotation. /// /// /// An interpolation parameter from 0 to 1. /// /// /// The index of the current spline segment. /// /// /// If enabled the node array will be treated as closed spline. /// /// /// An array of SplineNodes. /// /// /// Derivation /// public virtual Quaternion InterpolateRotation( Spline spline, double t, int index, bool autoClose, IList nodes, int derivationOrder ) { SplineNode n0; SplineNode n1; SplineNode n2; SplineNode n3; GetNodeData( nodes, index, autoClose, out n0, out n1, out n2, out n3 ); return InterpolateRotation( t, n0.Rotation, n1.Rotation, n2.Rotation, n3.Rotation, derivationOrder ); } /// /// Interpolates 4 vectors. /// /// /// The interpolated vector. /// /// /// An interpolation parameter from 0 to 1. /// /// /// 1st Vector /// /// /// 2nd Vector /// /// /// 3rd Vector /// /// /// 4th Vector /// /// /// Derivation order. /// public Vector3 InterpolateVector( double t, Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3, int derivationOrder ) { float b0; float b1; float b2; float b3; GetCoefficients( t, out b0, out b1, out b2, out b3, derivationOrder ); return b0 * v0 + b1 * v1 + b2 * v2 + b3 * v3; } /// /// Interpolates 4 float values. /// /// /// The interpolated float value. /// /// /// An interpolation parameter from 0 to 1. /// /// /// 1st float value /// /// /// 2nd float value /// /// /// 3rd float value /// /// /// 4th float value /// /// /// Derivation order. /// public float InterpolateValue( double t, float v0, float v1, float v2, float v3, int derivationOrder ) { float b0; float b1; float b2; float b3; GetCoefficients( t, out b0, out b1, out b2, out b3, derivationOrder ); return b0 * v0 + b1 * v1 + b2 * v2 + b3 * v3; } /// /// Interpolates 4 quaternions. /// /// /// The interpolated quaternion. /// /// /// An interpolation parameter from 0 to 1. /// /// /// 1st float quaternion /// /// /// 2nd float quaternion /// /// /// 3rd float quaternion /// /// /// 4th float quaternion /// /// /// Derivation order. /// public Quaternion InterpolateRotation( double t, Quaternion q0, Quaternion q1, Quaternion q2, Quaternion q3, int derivationOrder ) { float b0; float b1; float b2; float b3; if( Quaternion.Dot( q0, q1 ) < 0 ) q1 = q1.Negative( ); if( Quaternion.Dot( q1, q2 ) < 0 ) q2 = q2.Negative( ); if( Quaternion.Dot( q2, q3 ) < 0 ) q3 = q3.Negative( ); GetCoefficients( t, out b0, out b1, out b2, out b3, derivationOrder ); Vector3 imaginary0 = new Vector3( q0.x, q0.y, q0.z ); Vector3 imaginary1 = new Vector3( q1.x, q1.y, q1.z ); Vector3 imaginary2 = new Vector3( q2.x, q2.y, q2.z ); Vector3 imaginary3 = new Vector3( q3.x, q3.y, q3.z ); Vector3 interpolatedImaginary = b0 * imaginary0 + b1 * imaginary1 + b2 * imaginary2 + b3 * imaginary3; float interpolatedReal = b0 * q0.w + b1 * q1.w + b2 * q2.w + b3 * q3.w; Quaternion result = new Quaternion( interpolatedImaginary.x, interpolatedImaginary.y, interpolatedImaginary.z, interpolatedReal ); return result.Normalized( ); } /// /// Select 4 elements of an array according to the indices of nodes that are used for interpolation (nodeIndices). /// /// /// A generic array. /// /// /// The index of the currently processed spline node /// /// /// If set to true, the array will be treated as "looping array" / closed spline. /// /// /// 1st element. /// /// /// 2nd element. /// /// /// 3rd element. /// /// /// 4th element /// /// /// The type of the array's elements. /// public void GetNodeData( IList array, int index, bool autoClose, out T d0, out T d1, out T d2, out T d3 ) { int arrayLength = array.Count; d0 = array[GetNodeIndex( autoClose, arrayLength, index, nodeIndices[0] )]; d1 = array[GetNodeIndex( autoClose, arrayLength, index, nodeIndices[1] )]; d2 = array[GetNodeIndex( autoClose, arrayLength, index, nodeIndices[2] )]; d3 = array[GetNodeIndex( autoClose, arrayLength, index, nodeIndices[3] )]; } private int GetNodeIndex( bool autoClose, int arrayLength, int index, int offset ) { int idxNode = index + offset; if( autoClose ) return (idxNode % arrayLength + arrayLength) % arrayLength; else return Mathf.Clamp( idxNode, 0, arrayLength-1 ); } private void GetCoefficients( double t, out float b0, out float b1, out float b2, out float b3, int derivationOrder ) { switch( derivationOrder ) { case 0: GetCoefficients( t, out b0, out b1, out b2, out b3 ); return; case 1: GetCoefficientsFirstDerivative( t, out b0, out b1, out b2, out b3 ); return; case 2: GetCoefficientsSecondDerivative( t, out b0, out b1, out b2, out b3 ); return; } b0 = 0; b1 = 0; b2 = 0; b3 = 0; } private void GetCoefficients( double t, out float b0, out float b1, out float b2, out float b3 ) { double t2 = t * t; double t3 = t2 * t; b0 = (float) (coefficientMatrix[ 0] * t3 + coefficientMatrix[ 1] * t2 + coefficientMatrix[ 2] * t + coefficientMatrix[ 3]); b1 = (float) (coefficientMatrix[ 4] * t3 + coefficientMatrix[ 5] * t2 + coefficientMatrix[ 6] * t + coefficientMatrix[ 7]); b2 = (float) (coefficientMatrix[ 8] * t3 + coefficientMatrix[ 9] * t2 + coefficientMatrix[10] * t + coefficientMatrix[11]); b3 = (float) (coefficientMatrix[12] * t3 + coefficientMatrix[13] * t2 + coefficientMatrix[14] * t + coefficientMatrix[15]); } ///< Returns the interpolation coefficients for the curve private void GetCoefficientsFirstDerivative( double t, out float b0, out float b1, out float b2, out float b3 ) { double t2 = t * t; t = t * 2.0; t2 = t2 * 3.0; b0 = (float) (coefficientMatrix[ 0] * t2 + coefficientMatrix[ 1] * t + coefficientMatrix[ 2]); b1 = (float) (coefficientMatrix[ 4] * t2 + coefficientMatrix[ 5] * t + coefficientMatrix[ 6]); b2 = (float) (coefficientMatrix[ 8] * t2 + coefficientMatrix[ 9] * t + coefficientMatrix[10]); b3 = (float) (coefficientMatrix[12] * t2 + coefficientMatrix[13] * t + coefficientMatrix[14]); } ///< Returns the interpolation coefficients for the curve's first derivative private void GetCoefficientsSecondDerivative( double t, out float b0, out float b1, out float b2, out float b3 ) { t *= 6.0; b0 = (float) (coefficientMatrix[ 0] * t + coefficientMatrix[ 1] * 2f); b1 = (float) (coefficientMatrix[ 4] * t + coefficientMatrix[ 5] * 2f); b2 = (float) (coefficientMatrix[ 8] * t + coefficientMatrix[ 9] * 2f); b3 = (float) (coefficientMatrix[12] * t + coefficientMatrix[13] * 2f); } ///< Returns the interpolation coefficients for the curve's second derivative private void CheckMatrix( double[] coefficientMatrix ) { if( coefficientMatrix.Length != 16 ) throw new ArgumentException( "The coefficientMatrix-array must contain exactly 16 doubles!" ); } private void CheckIndices( int[] nodeIndices ) { if( nodeIndices.Length != 4 ) throw new ArgumentException( "nodeIndices-array must contain exactly 4 ints!" ); } } /// /// A Hermite Interpolator. /// /// /// In contrast to the other default SplineInterpolators this Interpolator provides special functions that are necessary for hermite spline interpolation /// public class HermiteInterpolator : SplineInterpolator { /// /// Initializes a new instance of the class. /// public HermiteInterpolator( ) { CoefficientMatrix = new double[] { 2.0, -3.0, 0.0, 1.0, -2.0, 3.0, 0.0, 0.0, 1.0, -2.0, 1.0, 0.0, 1.0, -1.0, 0.0, 0.0 }; NodeIndices = new int[] { 0, 1, -1, 2 }; } public override Vector3 InterpolateVector( Spline spline, double t, int index, bool autoClose, IList nodes, int derivationOrder ) { SplineNode n0; SplineNode n1; SplineNode n2; SplineNode n3; GetNodeData( nodes, index, autoClose, out n0, out n1, out n2, out n3 ); Vector3 v2 = n2.Position; Vector3 v3 = n3.Position; RecalcVectors( spline, n0, n1, ref v2, ref v3 ); return InterpolateVector( t, n0.Position, n1.Position, v2, v3, derivationOrder ); } public override Vector3 InterpolateVector( double t, int index, bool autoClose, IList nodes, int derivationOrder ) { Vector3 v0; Vector3 v1; Vector3 v2; Vector3 v3; GetNodeData( nodes, index, autoClose, out v0, out v1, out v2, out v3 ); RecalcVectors( v0, v1, ref v2, ref v3 ); return InterpolateVector( t, v0, v1, v2, v3, derivationOrder ); } public override float InterpolateValue( Spline spline, double t, int index, bool autoClose, IList nodes, int derivationOrder ) { SplineNode n0; SplineNode n1; SplineNode n2; SplineNode n3; GetNodeData( nodes, index, autoClose, out n0, out n1, out n2, out n3 ); float v2 = n2.CustomValue; float v3 = n3.CustomValue; RecalcScalars( spline, n0, n1, ref v2, ref v3 ); return InterpolateValue( t, n0.CustomValue, n1.CustomValue, v2, v3, derivationOrder ); } public override float InterpolateValue( double t, int index, bool autoClose, IList nodes, int derivationOrder ) { float v0; float v1; float v2; float v3; GetNodeData( nodes, index, autoClose, out v0, out v1, out v2, out v3 ); RecalcScalars( v0, v1, ref v2, ref v3 ); return InterpolateValue( t, v0, v1, v2, v3, derivationOrder ); } public override Quaternion InterpolateRotation( Spline spline, double t, int index, bool autoClose, IList nodes, int derivationOrder ) { SplineNode n0; SplineNode n1; SplineNode n2; SplineNode n3; GetNodeData( nodes, index, autoClose, out n0, out n1, out n2, out n3 ); Quaternion q2 = n2.Rotation; Quaternion q3 = n3.Rotation; RecalcRotations( n0.Rotation, n1.Rotation, ref q2, ref q3 ); return InterpolateRotation( t, n0.Rotation, n1.Rotation, q2, q3, derivationOrder ); } /// /// A function that returns the tangents for two spline nodes /// /// /// The used spline /// /// /// 1st node. /// /// /// 2nd node. /// /// /// In: Position of 3rd node. /// Out: Tangent of node0. /// /// /// In: Position of 4th node. /// Out: Tangent of node1. /// public void RecalcVectors( Spline spline, SplineNode node0, SplineNode node1, ref Vector3 P2, ref Vector3 P3 ) { float tension0; float tension1; if( spline.perNodeTension ) { tension0 = node0.tension; tension1 = node1.tension; } else { tension0 = spline.tension; tension1 = spline.tension; } if( spline.tangentMode == Spline.TangentMode.UseNodeForwardVector ) { P2 = node0.transform.forward * tension0; P3 = node1.transform.forward * tension1; } else { P2 = node1.Position - P2; P3 = P3 - node0.Position; if( spline.tangentMode != Spline.TangentMode.UseTangents ) { P2.Normalize( ); P3.Normalize( ); } P2 = P2 * tension0; P3 = P3 * tension1; } } /// /// A function that returns the tangents for two spline nodes. /// /// /// Position of 1st spline node. /// /// /// Position of 2nd spline node. /// /// /// In: Position of 3rd node. /// Out: Tangent of node0. /// /// /// In: Position of 4th node. /// Out: Tangent of node1. /// public void RecalcVectors( Vector3 P0, Vector3 P1, ref Vector3 P2, ref Vector3 P3 ) { float tension = 0.5f; P2 = P1 - P2; P3 = P3 - P0; P2 = P2 * tension; P3 = P3 * tension; } /// /// A function that returns the tangents for two spline nodes /// /// /// The used spline /// /// /// 1st node. /// /// /// 2nd node. /// /// /// In: Value of 3rd node. /// Out: Tangent of node0. /// /// /// In: Value of 4th node. /// Out: Tangent of node1. /// public void RecalcScalars( Spline spline, SplineNode node0, SplineNode node1, ref float P2, ref float P3 ) { float tension0; float tension1; if( spline.perNodeTension ) { tension0 = node0.tension; tension1 = node1.tension; } else { tension0 = spline.tension; tension1 = spline.tension; } P2 = node1.customValue - P2; P3 = P3 - node0.customValue; P2 = P2 * tension0; P3 = P3 * tension1; } /// /// A function that returns the tangents for two spline nodes. /// /// /// Value of 1st spline node. /// /// /// Value of 2nd spline node. /// /// /// In: Value of 3rd node. /// Out: Tangent of node0. /// /// /// In: Value of 4th node. /// Out: Tangent of node1. /// public void RecalcScalars( float P0, float P1, ref float P2, ref float P3 ) { float tension = 0.5f; P2 = P1 - P2; P3 = P3 - P0; P2 = P2 * tension; P3 = P3 * tension; } /// /// A function that returns the intermediate rotations for two spline nodes /// /// /// The rotation of the 1st spline node. /// /// /// The rotation of the 2nd spline node. /// /// /// In: Rotation of the 3rd spline node. /// Out: intermediate rotation of node0. /// /// /// In: Rotation of the 4th spline node. /// Out: intermediate rotation of node1. /// public void RecalcRotations( Quaternion Q0, Quaternion Q1, ref Quaternion Q2, ref Quaternion Q3 ) { //Recalc rotations Q2 = QuaternionUtils.GetSquadIntermediate( Q0, Q1, Q2 ); Q3 = QuaternionUtils.GetSquadIntermediate( Q1, Q2, Q3 ); } } /// /// A Bezier Interpolator. /// public class BezierInterpolator : SplineInterpolator { /// /// Initializes a new instance of the class. /// public BezierInterpolator( ) { CoefficientMatrix = new double[] { -1.0, 3.0, -3.0, 1.0, 3.0, -6.0, 3.0, 0.0, -3.0, 3.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 }; NodeIndices = new int[] { 0, 1, 2, 3 }; } } /// /// A B-spline Interpolator. /// public class BSplineInterpolator : SplineInterpolator { /// /// Initializes a new instance of the class. /// public BSplineInterpolator( ) { CoefficientMatrix = new double[] { -1.0/6.0, 3.0/6.0, - 3.0/6.0, 1.0/6.0, 3.0/6.0, - 6.0/6.0, 0.0/6.0, 4.0/6.0, -3.0/6.0, 3.0/6.0, 3.0/6.0, 1.0/6.0, 1.0/6.0, 0.0/6.0, 0.0/6.0, 0.0/6.0 }; NodeIndices = new int[] { -1, 0, 1, 2 }; } } /// /// A Linear Interpolator. /// public class LinearInterpolator : SplineInterpolator { /// /// Initializes a new instance of the class. /// public LinearInterpolator( ) { CoefficientMatrix = new double[] { 0.0, 0.0, - 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; NodeIndices = new int[] { 0, 1, 2, 3 }; } }