using System; using System.Collections; using System.Collections.Generic; using Unity.Collections; using Unity.Mathematics; namespace UnityEngine.Splines { /// /// A readonly representation of that is optimized for efficient access and queries. /// NativeSpline can be constructed with a Spline and Transform. If a transform is applied, all values will be /// relative to the transformed knot positions. /// /// /// NativeSpline is compatible with the job system. /// public struct NativeSpline : ISpline, IDisposable { NativeArray m_Knots; // As we cannot make a NativeArray of NativeArray all segments lookup tables are stored in a single array // each lookup table as a length of k_SegmentResolution and starts at index i = curveIndex * k_SegmentResolution NativeArray m_SegmentLengthsLookupTable; bool m_Closed; float m_Length; const int k_SegmentResolution = 30; /// /// A NativeArray of that form this Spline. /// /// /// Returns a reference to the knots array. /// public NativeArray Knots => m_Knots; /// /// Whether the spline is open (has a start and end point) or closed (forms an unbroken loop). /// public bool Closed => m_Closed; /// /// Return the number of knots. /// public int Count => m_Knots.Length; /// /// Return the sum of all curve lengths, accounting for state. /// Note that this value is affected by the transform used to create this NativeSpline. /// /// /// Returns the sum length of all curves composing this spline, accounting for closed state. /// public float GetLength() => m_Length; /// /// Get the knot at . /// /// The zero-based index of the knot. public BezierKnot this[int index] => m_Knots[index]; /// /// Get an enumerator that iterates through the collection. /// /// An IEnumerator that is used to iterate the collection. public IEnumerator GetEnumerator() => m_Knots.GetEnumerator(); /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Create a new NativeSpline from a set of . /// /// The object to convert to a . /// The memory allocation method to use when reserving space for native arrays. public NativeSpline(ISpline spline, Allocator allocator = Allocator.Temp) : this(spline, spline.Closed, float4x4.identity, allocator) { } /// /// Create a new NativeSpline from a set of . /// /// The object to convert to a . /// A transform matrix to be applied to the spline knots and tangents. /// The memory allocation method to use when reserving space for native arrays. public NativeSpline(ISpline spline, float4x4 transform, Allocator allocator = Allocator.Temp) : this(spline, spline.Closed, transform, allocator) { } /// /// Create a new NativeSpline from a set of . /// /// A collection of sequential forming the spline path. /// Whether the spline is open (has a start and end point) or closed (forms an unbroken loop). /// Apply a transformation matrix to the control . /// The memory allocation method to use when reserving space for native arrays. public NativeSpline(IReadOnlyList knots, bool closed, float4x4 transform, Allocator allocator = Allocator.Temp) { int kc = knots.Count; m_Knots = new NativeArray(knots.Count, allocator); for (int i = 0; i < kc; i++) m_Knots[i] = knots[i].Transform(transform); m_Closed = closed; m_Length = 0f; int curveCount = m_Closed ? kc : kc - 1; // As we cannot make a NativeArray of NativeArray all segments lookup tables are stored in a single array // each lookup table as a length of k_SegmentResolution and starts at index i = curveIndex * k_SegmentResolution m_SegmentLengthsLookupTable = new NativeArray(knots.Count * k_SegmentResolution, allocator); m_Length = 0f; DistanceToInterpolation[] distanceToTimes = new DistanceToInterpolation[k_SegmentResolution]; for (int i = 0; i < curveCount; i++) { CurveUtility.CalculateCurveLengths(GetCurve(i), distanceToTimes); m_Length += distanceToTimes[k_SegmentResolution - 1].Distance; for(int distanceToTimeIndex = 0; distanceToTimeIndex < k_SegmentResolution; distanceToTimeIndex++) m_SegmentLengthsLookupTable[i * k_SegmentResolution + distanceToTimeIndex] = distanceToTimes[distanceToTimeIndex]; } } /// /// Get a from a knot index. /// /// The knot index that serves as the first control point for this curve. /// /// A formed by the knot at index and the next knot. /// public BezierCurve GetCurve(int index) { int next = m_Closed ? (index + 1) % Count : math.min(index + 1, Count - 1); return new BezierCurve(m_Knots[index], m_Knots[next]); } /// /// Get the length of a . /// /// The 0 based index of the curve to find length for. /// The length of the bezier curve at index. public float GetCurveLength(int curveIndex) => m_SegmentLengthsLookupTable[curveIndex * k_SegmentResolution + k_SegmentResolution - 1].Distance; /// /// Release allocated resources. /// public void Dispose() { m_Knots.Dispose(); m_SegmentLengthsLookupTable.Dispose(); } struct Slice : IReadOnlyList where T : struct { NativeSlice m_Slice; public Slice(NativeArray array, int start, int count) { m_Slice = new NativeSlice(array, start, count); } public IEnumerator GetEnumerator() => m_Slice.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public int Count => m_Slice.Length; public T this[int index] => m_Slice[index]; } /// /// Return the normalized interpolation (t) corresponding to a distance on a . /// /// The zero-based index of the curve. /// The curve-relative distance to convert to an interpolation ratio (also referred to as 't'). /// The normalized interpolation ratio associated to distance on the designated curve. public float GetCurveInterpolation(int curveIndex, float curveDistance) { if(curveIndex <0 || curveIndex >= m_SegmentLengthsLookupTable.Length || curveDistance <= 0) return 0f; var curveLength = GetCurveLength(curveIndex); if(curveDistance >= curveLength) return 1f; var startIndex = curveIndex * k_SegmentResolution; var slice = new Slice(m_SegmentLengthsLookupTable, startIndex, k_SegmentResolution); return CurveUtility.GetDistanceToInterpolation(slice, curveDistance); } } }