179 lines
8.7 KiB
C#
179 lines
8.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Unity.Collections;
|
|
using Unity.Mathematics;
|
|
|
|
namespace UnityEngine.Splines
|
|
{
|
|
/// <summary>
|
|
/// A readonly representation of <see cref="Spline"/> 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.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// NativeSpline is compatible with the job system.
|
|
/// </remarks>
|
|
public struct NativeSpline : ISpline, IDisposable
|
|
{
|
|
NativeArray<BezierKnot> 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<DistanceToInterpolation> m_SegmentLengthsLookupTable;
|
|
bool m_Closed;
|
|
float m_Length;
|
|
const int k_SegmentResolution = 30;
|
|
|
|
/// <summary>
|
|
/// A NativeArray of <see cref="BezierKnot"/> that form this Spline.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Returns a reference to the knots array.
|
|
/// </returns>
|
|
public NativeArray<BezierKnot> Knots => m_Knots;
|
|
|
|
/// <summary>
|
|
/// Whether the spline is open (has a start and end point) or closed (forms an unbroken loop).
|
|
/// </summary>
|
|
public bool Closed => m_Closed;
|
|
|
|
/// <summary>
|
|
/// Return the number of knots.
|
|
/// </summary>
|
|
public int Count => m_Knots.Length;
|
|
|
|
/// <summary>
|
|
/// Return the sum of all curve lengths, accounting for <see cref="Closed"/> state.
|
|
/// Note that this value is affected by the transform used to create this NativeSpline.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Returns the sum length of all curves composing this spline, accounting for closed state.
|
|
/// </returns>
|
|
public float GetLength() => m_Length;
|
|
|
|
/// <summary>
|
|
/// Get the knot at <paramref name="index"/>.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based index of the knot.</param>
|
|
public BezierKnot this[int index] => m_Knots[index];
|
|
|
|
/// <summary>
|
|
/// Get an enumerator that iterates through the <see cref="BezierKnot"/> collection.
|
|
/// </summary>
|
|
/// <returns>An IEnumerator that is used to iterate the <see cref="BezierKnot"/> collection.</returns>
|
|
public IEnumerator<BezierKnot> GetEnumerator() => m_Knots.GetEnumerator();
|
|
|
|
/// <inheritdoc cref="GetEnumerator"/>
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
/// <summary>
|
|
/// Create a new NativeSpline from a set of <see cref="BezierKnot"/>.
|
|
/// </summary>
|
|
/// <param name="spline">The <see cref="ISpline"/> object to convert to a <see cref="NativeSpline"/>.</param>
|
|
/// <param name="allocator">The memory allocation method to use when reserving space for native arrays.</param>
|
|
public NativeSpline(ISpline spline, Allocator allocator = Allocator.Temp)
|
|
: this(spline, spline.Closed, float4x4.identity, allocator) { }
|
|
|
|
/// <summary>
|
|
/// Create a new NativeSpline from a set of <see cref="BezierKnot"/>.
|
|
/// </summary>
|
|
/// <param name="spline">The <see cref="ISpline"/> object to convert to a <see cref="NativeSpline"/>.</param>
|
|
/// <param name="transform">A transform matrix to be applied to the spline knots and tangents.</param>
|
|
/// <param name="allocator">The memory allocation method to use when reserving space for native arrays.</param>
|
|
public NativeSpline(ISpline spline, float4x4 transform, Allocator allocator = Allocator.Temp)
|
|
: this(spline, spline.Closed, transform, allocator) { }
|
|
|
|
/// <summary>
|
|
/// Create a new NativeSpline from a set of <see cref="BezierKnot"/>.
|
|
/// </summary>
|
|
/// <param name="knots">A collection of sequential <see cref="BezierKnot"/> forming the spline path.</param>
|
|
/// <param name="closed">Whether the spline is open (has a start and end point) or closed (forms an unbroken loop).</param>
|
|
/// <param name="transform">Apply a transformation matrix to the control <see cref="Knots"/>.</param>
|
|
/// <param name="allocator">The memory allocation method to use when reserving space for native arrays.</param>
|
|
public NativeSpline(IReadOnlyList<BezierKnot> knots, bool closed, float4x4 transform, Allocator allocator = Allocator.Temp)
|
|
{
|
|
int kc = knots.Count;
|
|
m_Knots = new NativeArray<BezierKnot>(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<DistanceToInterpolation>(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];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a <see cref="BezierCurve"/> from a knot index.
|
|
/// </summary>
|
|
/// <param name="index">The knot index that serves as the first control point for this curve.</param>
|
|
/// <returns>
|
|
/// A <see cref="BezierCurve"/> formed by the knot at index and the next knot.
|
|
/// </returns>
|
|
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]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the length of a <see cref="BezierCurve"/>.
|
|
/// </summary>
|
|
/// <param name="curveIndex">The 0 based index of the curve to find length for.</param>
|
|
/// <returns>The length of the bezier curve at index.</returns>
|
|
public float GetCurveLength(int curveIndex) => m_SegmentLengthsLookupTable[curveIndex * k_SegmentResolution + k_SegmentResolution - 1].Distance;
|
|
|
|
/// <summary>
|
|
/// Release allocated resources.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
m_Knots.Dispose();
|
|
m_SegmentLengthsLookupTable.Dispose();
|
|
}
|
|
|
|
struct Slice<T> : IReadOnlyList<T> where T : struct
|
|
{
|
|
NativeSlice<T> m_Slice;
|
|
public Slice(NativeArray<T> array, int start, int count) { m_Slice = new NativeSlice<T>(array, start, count); }
|
|
public IEnumerator<T> GetEnumerator() => m_Slice.GetEnumerator();
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
public int Count => m_Slice.Length;
|
|
public T this[int index] => m_Slice[index];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the normalized interpolation (t) corresponding to a distance on a <see cref="BezierCurve"/>.
|
|
/// </summary>
|
|
/// <param name="curveIndex"> The zero-based index of the curve.</param>
|
|
/// <param name="curveDistance">The curve-relative distance to convert to an interpolation ratio (also referred to as 't').</param>
|
|
/// <returns> The normalized interpolation ratio associated to distance on the designated curve.</returns>
|
|
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<DistanceToInterpolation>(m_SegmentLengthsLookupTable, startIndex, k_SegmentResolution);
|
|
return CurveUtility.GetDistanceToInterpolation(slice, curveDistance);
|
|
}
|
|
}
|
|
}
|