HeavenStudio/Assets/Plugins/BezierSolution/Other/BezierDataStructures.cs

344 lines
12 KiB
C#

using UnityEngine;
namespace BezierSolution
{
public enum SplineAutoConstructMode { None = 0, Linear = 1, Smooth1 = 2, Smooth2 = 3 };
[System.Flags]
internal enum InternalDirtyFlags
{
None = 0,
EndPointTransformChange = 1 << 1,
ControlPointPositionChange = 1 << 2,
NormalChange = 1 << 3,
NormalOffsetChange = 1 << 4,
ExtraDataChange = 1 << 5,
All = EndPointTransformChange | ControlPointPositionChange | NormalChange | NormalOffsetChange | ExtraDataChange
};
[System.Flags]
public enum DirtyFlags
{
None = 0,
SplineShapeChanged = 1 << 1,
NormalsChanged = 1 << 2,
ExtraDataChanged = 1 << 3,
All = SplineShapeChanged | NormalsChanged | ExtraDataChanged
};
[System.Flags]
public enum PointCacheFlags
{
None = 0,
Positions = 1 << 1,
Normals = 1 << 2,
Tangents = 1 << 3,
Bitangents = 1 << 4,
ExtraDatas = 1 << 5,
All = Positions | Normals | Tangents | Bitangents | ExtraDatas
};
public delegate void SplineChangeDelegate( BezierSpline spline, DirtyFlags dirtyFlags );
public delegate BezierPoint.ExtraData ExtraDataLerpFunction( BezierPoint.ExtraData data1, BezierPoint.ExtraData data2, float normalizedT );
public partial class BezierSpline
{
private static readonly ExtraDataLerpFunction defaultExtraDataLerpFunction = BezierPoint.ExtraData.LerpUnclamped;
public struct Segment
{
public readonly BezierPoint point1, point2;
public readonly float localT;
public Segment( BezierPoint point1, BezierPoint point2, float localT )
{
this.point1 = point1;
this.point2 = point2;
this.localT = localT;
}
public float GetNormalizedT() { return GetNormalizedT( localT ); }
public float GetNormalizedT( float localT )
{
BezierSpline spline = point1.spline;
return ( point1.index + localT ) / ( spline.m_loop ? spline.Count : ( spline.Count - 1 ) );
}
public Vector3 GetPoint() { return GetPoint( localT ); }
public Vector3 GetPoint( float localT )
{
float oneMinusLocalT = 1f - localT;
return oneMinusLocalT * oneMinusLocalT * oneMinusLocalT * point1.position +
3f * oneMinusLocalT * oneMinusLocalT * localT * point1.followingControlPointPosition +
3f * oneMinusLocalT * localT * localT * point2.precedingControlPointPosition +
localT * localT * localT * point2.position;
}
public Vector3 GetTangent() { return GetTangent( localT ); }
public Vector3 GetTangent( float localT )
{
float oneMinusLocalT = 1f - localT;
return 3f * oneMinusLocalT * oneMinusLocalT * ( point1.followingControlPointPosition - point1.position ) +
6f * oneMinusLocalT * localT * ( point2.precedingControlPointPosition - point1.followingControlPointPosition ) +
3f * localT * localT * ( point2.position - point2.precedingControlPointPosition );
}
public Vector3 GetNormal() { return GetNormal( localT ); }
public Vector3 GetNormal( float localT )
{
Vector3[] intermediateNormals = point1.intermediateNormals;
if( intermediateNormals != null && intermediateNormals.Length > 0 )
{
localT = Mathf.Clamp01( localT ) * ( intermediateNormals.Length - 1 );
int localStartIndex = (int) localT;
return ( localStartIndex < intermediateNormals.Length - 1 ) ? Vector3.LerpUnclamped( intermediateNormals[localStartIndex], intermediateNormals[localStartIndex + 1], localT - localStartIndex ) : intermediateNormals[localStartIndex];
}
Vector3 startNormal = point1.normal;
Vector3 endNormal = point2.normal;
Vector3 normal = Vector3.LerpUnclamped( startNormal, endNormal, localT );
if( normal.y == 0f && normal.x == 0f && normal.z == 0f )
{
// Don't return Vector3.zero as normal
normal = Vector3.LerpUnclamped( startNormal, endNormal, localT > 0.01f ? ( localT - 0.01f ) : ( localT + 0.01f ) );
if( normal.y == 0f && normal.x == 0f && normal.z == 0f )
normal = Vector3.up;
}
return normal;
}
public BezierPoint.ExtraData GetExtraData() { return defaultExtraDataLerpFunction( point1.extraData, point2.extraData, localT ); }
public BezierPoint.ExtraData GetExtraData( float localT ) { return defaultExtraDataLerpFunction( point1.extraData, point2.extraData, localT ); }
public BezierPoint.ExtraData GetExtraData( ExtraDataLerpFunction lerpFunction ) { return lerpFunction( point1.extraData, point2.extraData, localT ); }
public BezierPoint.ExtraData GetExtraData( float localT, ExtraDataLerpFunction lerpFunction ) { return lerpFunction( point1.extraData, point2.extraData, localT ); }
}
public class EvenlySpacedPointsHolder
{
public readonly BezierSpline spline;
public readonly float splineLength;
public readonly float[] uniformNormalizedTs;
public EvenlySpacedPointsHolder( BezierSpline spline, float splineLength, float[] uniformNormalizedTs )
{
this.spline = spline;
this.splineLength = splineLength;
this.uniformNormalizedTs = uniformNormalizedTs;
}
public float GetNormalizedTAtDistance( float distance )
{
return GetNormalizedTAtPercentage( distance / splineLength );
}
public float GetNormalizedTAtPercentage( float percentage )
{
if( !spline.loop )
{
if( percentage <= 0f )
return 0f;
else if( percentage >= 1f )
return 1f;
}
else
{
while( percentage < 0f )
percentage += 1f;
while( percentage >= 1f )
percentage -= 1f;
}
float indexRaw = ( uniformNormalizedTs.Length - 1 ) * percentage;
int index = (int) indexRaw;
return Mathf.LerpUnclamped( uniformNormalizedTs[index], uniformNormalizedTs[index + 1], indexRaw - index );
}
public float GetPercentageAtNormalizedT( float normalizedT )
{
if( !spline.loop )
{
if( normalizedT <= 0f )
return 0f;
else if( normalizedT >= 1f )
return 1f;
}
else
{
if( normalizedT < 0f )
normalizedT += 1f;
if( normalizedT >= 1f )
normalizedT -= 1f;
}
// Perform binary search
int lowerBound = 0;
int upperBound = uniformNormalizedTs.Length - 1;
while( lowerBound <= upperBound )
{
int index = lowerBound + ( ( upperBound - lowerBound ) >> 1 );
float arrElement = uniformNormalizedTs[index];
if( arrElement < normalizedT )
lowerBound = index + 1;
else if( arrElement > normalizedT )
upperBound = index - 1;
else
return index / (float) ( uniformNormalizedTs.Length - 1 );
}
float inverseLerp = ( normalizedT - uniformNormalizedTs[lowerBound] ) / ( uniformNormalizedTs[lowerBound - 1] - uniformNormalizedTs[lowerBound] );
return ( lowerBound - inverseLerp ) / ( uniformNormalizedTs.Length - 1 );
}
}
public class PointCache
{
public readonly Vector3[] positions, normals, tangents, bitangents;
public readonly BezierPoint.ExtraData[] extraDatas;
public readonly bool loop;
public PointCache( Vector3[] positions, Vector3[] normals, Vector3[] tangents, Vector3[] bitangents, BezierPoint.ExtraData[] extraDatas, bool loop )
{
this.positions = positions;
this.normals = normals;
this.tangents = tangents;
this.bitangents = bitangents;
this.extraDatas = extraDatas;
this.loop = loop;
}
public Vector3 GetPoint( float percentage ) { return LerpArray( positions, percentage ); }
public Vector3 GetNormal( float percentage ) { return LerpArray( normals, percentage ); }
public Vector3 GetTangent( float percentage ) { return LerpArray( tangents, percentage ); }
public Vector3 GetBitangent( float percentage ) { return LerpArray( bitangents, percentage ); }
public BezierPoint.ExtraData GetExtraData( float percentage ) { return GetExtraData( percentage, defaultExtraDataLerpFunction ); }
public BezierPoint.ExtraData GetExtraData( float percentage, ExtraDataLerpFunction lerpFunction )
{
if( !loop )
{
if( percentage <= 0f )
return extraDatas[0];
else if( percentage >= 1f )
return extraDatas[extraDatas.Length - 1];
}
else
{
if( percentage < 0f )
percentage += 1f;
if( percentage >= 1f )
percentage -= 1f;
}
float t = percentage * ( loop ? extraDatas.Length : ( extraDatas.Length - 1 ) );
int startIndex = (int) t;
int endIndex = startIndex + 1;
if( endIndex == extraDatas.Length )
endIndex = 0;
return lerpFunction( extraDatas[startIndex], extraDatas[endIndex], t - startIndex );
}
private Vector3 LerpArray( Vector3[] array, float percentage )
{
if( !loop )
{
if( percentage <= 0f )
return array[0];
else if( percentage >= 1f )
return array[array.Length - 1];
}
else
{
if( percentage < 0f )
percentage += 1f;
if( percentage >= 1f )
percentage -= 1f;
}
float t = percentage * ( loop ? array.Length : ( array.Length - 1 ) );
int startIndex = (int) t;
int endIndex = startIndex + 1;
if( endIndex == array.Length )
endIndex = 0;
return Vector3.LerpUnclamped( array[startIndex], array[endIndex], t - startIndex );
}
}
}
public partial class BezierPoint
{
public enum HandleMode { Free, Aligned, Mirrored };
[System.Serializable]
public struct ExtraData
{
public float c1, c2, c3, c4;
public ExtraData( float c1 = 0f, float c2 = 0f, float c3 = 0f, float c4 = 0f )
{
this.c1 = c1;
this.c2 = c2;
this.c3 = c3;
this.c4 = c4;
}
public static ExtraData Lerp( ExtraData a, ExtraData b, float t )
{
t = Mathf.Clamp01( t );
return new ExtraData(
a.c1 + ( b.c1 - a.c1 ) * t,
a.c2 + ( b.c2 - a.c2 ) * t,
a.c3 + ( b.c3 - a.c3 ) * t,
a.c4 + ( b.c4 - a.c4 ) * t );
}
public static ExtraData LerpUnclamped( ExtraData a, ExtraData b, float t )
{
return new ExtraData(
a.c1 + ( b.c1 - a.c1 ) * t,
a.c2 + ( b.c2 - a.c2 ) * t,
a.c3 + ( b.c3 - a.c3 ) * t,
a.c4 + ( b.c4 - a.c4 ) * t );
}
public static implicit operator ExtraData( Vector2 v ) { return new ExtraData( v.x, v.y ); }
public static implicit operator ExtraData( Vector3 v ) { return new ExtraData( v.x, v.y, v.z ); }
public static implicit operator ExtraData( Vector4 v ) { return new ExtraData( v.x, v.y, v.z, v.w ); }
public static implicit operator ExtraData( Quaternion q ) { return new ExtraData( q.x, q.y, q.z, q.w ); }
public static implicit operator ExtraData( Rect r ) { return new ExtraData( r.xMin, r.yMin, r.width, r.height ); }
#if UNITY_2017_2_OR_NEWER
public static implicit operator ExtraData( Vector2Int v ) { return new ExtraData( v.x, v.y ); }
public static implicit operator ExtraData( Vector3Int v ) { return new ExtraData( v.x, v.y, v.z ); }
public static implicit operator ExtraData( RectInt r ) { return new ExtraData( r.xMin, r.yMin, r.width, r.height ); }
#endif
public static implicit operator Vector2( ExtraData v ) { return new Vector2( v.c1, v.c2 ); }
public static implicit operator Vector3( ExtraData v ) { return new Vector3( v.c1, v.c2, v.c3 ); }
public static implicit operator Vector4( ExtraData v ) { return new Vector4( v.c1, v.c2, v.c3, v.c4 ); }
public static implicit operator Quaternion( ExtraData v ) { return new Quaternion( v.c1, v.c2, v.c3, v.c4 ); }
public static implicit operator Rect( ExtraData v ) { return new Rect( v.c1, v.c2, v.c3, v.c4 ); }
#if UNITY_2017_2_OR_NEWER
public static implicit operator Vector2Int( ExtraData v ) { return new Vector2Int( Mathf.RoundToInt( v.c1 ), Mathf.RoundToInt( v.c2 ) ); }
public static implicit operator Vector3Int( ExtraData v ) { return new Vector3Int( Mathf.RoundToInt( v.c1 ), Mathf.RoundToInt( v.c2 ), Mathf.RoundToInt( v.c3 ) ); }
public static implicit operator RectInt( ExtraData v ) { return new RectInt( Mathf.RoundToInt( v.c1 ), Mathf.RoundToInt( v.c2 ), Mathf.RoundToInt( v.c3 ), Mathf.RoundToInt( v.c4 ) ); }
#endif
public static bool operator ==( ExtraData d1, ExtraData d2 ) { return d1.c1 == d2.c1 && d1.c2 == d2.c2 && d1.c3 == d2.c3 && d1.c4 == d2.c4; }
public static bool operator !=( ExtraData d1, ExtraData d2 ) { return d1.c1 != d2.c1 || d1.c2 != d2.c2 || d1.c3 != d2.c3 || d1.c4 != d2.c4; }
public override bool Equals( object obj ) { return obj is ExtraData && this == (ExtraData) obj; }
public override int GetHashCode() { return unchecked((int) ( ( ( ( 17 * 23 + c1 ) * 23 + c2 ) * 23 + c3 ) * 23 + c4 )); }
public override string ToString() { return ( (Vector4) this ).ToString(); }
}
}
}