using System; using System.Runtime.CompilerServices; using Unity.Collections; using static Unity.Mathematics.math; namespace UnityEngine.XR.Interaction.Toolkit.Utilities.Collections { /// /// Burst friendly curve implementation used to efficiently work with animation curves in the job system. /// public struct NativeCurve : IDisposable { /// /// Informs if the native data structure has an allocated memory buffer. /// public bool isCreated => m_Values.IsCreated; NativeArray m_Values; WrapMode m_PreWrapMode; WrapMode m_PostWrapMode; void InitializeValues(int count, Allocator allocator = Allocator.Persistent) { if (m_Values.IsCreated) m_Values.Dispose(); m_Values = new NativeArray(count, allocator, NativeArrayOptions.UninitializedMemory); } /// /// Re-initialize native curve data with new Animation curve. /// /// Curve ground truth to initialize from. /// Number of samples to use when converting from animation curve to native curve. public void Update(AnimationCurve curve, int resolution) { if (curve == null) return; m_PreWrapMode = curve.preWrapMode; m_PostWrapMode = curve.postWrapMode; if (!m_Values.IsCreated || m_Values.Length != resolution) InitializeValues(resolution); for (int i = 0; i < resolution; i++) m_Values[i] = curve.Evaluate((float)i / (float)resolution); } /// /// Evaluate value along the underlying native curve. /// /// Location along curve to evaluate. /// Value along curve at given location t. public float Evaluate(float t) { var count = m_Values.Length; if (count == 1) return m_Values[0]; if (t < 0f) { switch (m_PreWrapMode) { default: return m_Values[0]; case WrapMode.Loop: t = 1f - (abs(t) % 1f); break; case WrapMode.PingPong: t = PingPong(t, 1f); break; } } else if (t > 1f) { switch (m_PostWrapMode) { default: return m_Values[count - 1]; case WrapMode.Loop: t %= 1f; break; case WrapMode.PingPong: t = PingPong(t, 1f); break; } } var it = t * (count - 1); var lower = (int)it; var upper = lower + 1; if (upper >= count) upper = count - 1; return lerp(m_Values[lower], m_Values[upper], it - lower); } /// /// Dispose native collection. /// public void Dispose() { if (m_Values.IsCreated) m_Values.Dispose(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] float Repeat(float t, float length) { return clamp(t - floor(t / length) * length, 0, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] float PingPong(float t, float length) { t = Repeat(t, length * 2f); return length - abs(t - length); } } }