895 lines
26 KiB
C#
895 lines
26 KiB
C#
|
using UnityEngine;
|
||
|
|
||
|
namespace Dreamteck.Splines {
|
||
|
[ExecuteInEditMode]
|
||
|
public class SplineUser : MonoBehaviour, ISerializationCallbackReceiver
|
||
|
{
|
||
|
public enum UpdateMethod { Update, FixedUpdate, LateUpdate }
|
||
|
[HideInInspector]
|
||
|
public UpdateMethod updateMethod = UpdateMethod.Update;
|
||
|
|
||
|
public SplineComputer spline
|
||
|
{
|
||
|
get {
|
||
|
return _spline;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _spline)
|
||
|
{
|
||
|
if (_spline != null)
|
||
|
{
|
||
|
_spline.Unsubscribe(this);
|
||
|
}
|
||
|
_spline = value;
|
||
|
if (_spline != null)
|
||
|
{
|
||
|
_spline.Subscribe(this);
|
||
|
Rebuild();
|
||
|
}
|
||
|
OnSplineChanged();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public double clipFrom
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _clipFrom;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _clipFrom)
|
||
|
{
|
||
|
animClipFrom = (float)_clipFrom;
|
||
|
_clipFrom = DMath.Clamp01(value);
|
||
|
if (_clipFrom > _clipTo)
|
||
|
{
|
||
|
if (!_spline.isClosed) _clipTo = _clipFrom;
|
||
|
}
|
||
|
getSamples = true;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public double clipTo
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _clipTo;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
|
||
|
if (value != _clipTo)
|
||
|
{
|
||
|
animClipTo = (float)_clipTo;
|
||
|
_clipTo = DMath.Clamp01(value);
|
||
|
if (_clipTo < _clipFrom)
|
||
|
{
|
||
|
if (!_spline.isClosed) _clipFrom = _clipTo;
|
||
|
}
|
||
|
getSamples = true;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool autoUpdate
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _autoUpdate;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _autoUpdate)
|
||
|
{
|
||
|
_autoUpdate = value;
|
||
|
if (value) Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool loopSamples
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _loopSamples;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _loopSamples)
|
||
|
{
|
||
|
_loopSamples = value;
|
||
|
if(!_loopSamples && _clipTo < _clipFrom)
|
||
|
{
|
||
|
double temp = _clipTo;
|
||
|
_clipTo = _clipFrom;
|
||
|
_clipFrom = temp;
|
||
|
}
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//The percent of the spline that we're traversing
|
||
|
public double span
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (samplesAreLooped) return (1.0 - _clipFrom) + _clipTo;
|
||
|
return _clipTo - _clipFrom;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool samplesAreLooped
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _loopSamples && _clipFrom >= _clipTo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public RotationModifier rotationModifier
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _rotationModifier;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public OffsetModifier offsetModifier
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _offsetModifier;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public ColorModifier colorModifier
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _colorModifier;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SizeModifier sizeModifier
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _sizeModifier;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Serialized values
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private SplineComputer _spline;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _autoUpdate = true;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
protected RotationModifier _rotationModifier = new RotationModifier();
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
protected OffsetModifier _offsetModifier = new OffsetModifier();
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
protected ColorModifier _colorModifier = new ColorModifier();
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
protected SizeModifier _sizeModifier = new SizeModifier();
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private SplineSample _clipFromSample = new SplineSample(), _clipToSample = new SplineSample();
|
||
|
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _loopSamples = false;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private double _clipFrom = 0.0;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private double _clipTo = 1.0;
|
||
|
|
||
|
//float values used for making animations
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private float animClipFrom = 0f;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private float animClipTo = 1f;
|
||
|
|
||
|
private SampleCollection _sampleCollection = new SampleCollection();
|
||
|
private bool rebuild = false, getSamples = false, postBuild = false;
|
||
|
private Transform _trs = null;
|
||
|
private bool _hasTransform = false;
|
||
|
private SplineSample _workSample = new SplineSample();
|
||
|
#if UNITY_EDITOR
|
||
|
private bool _isPlaying = false;
|
||
|
protected bool isPlaying => _isPlaying;
|
||
|
#endif
|
||
|
|
||
|
protected Transform trs
|
||
|
{
|
||
|
get { return _trs; }
|
||
|
}
|
||
|
protected bool hasTransform
|
||
|
{
|
||
|
get { return _hasTransform; }
|
||
|
}
|
||
|
public int sampleCount
|
||
|
{
|
||
|
get { return _sampleCount; }
|
||
|
}
|
||
|
|
||
|
private int _sampleCount = 0, _startSampleIndex = 0;
|
||
|
/// <summary>
|
||
|
/// Use this to work with the Evaluate and Project methods
|
||
|
/// </summary>
|
||
|
protected SplineSample evalResult = new SplineSample();
|
||
|
|
||
|
//Threading values
|
||
|
[HideInInspector]
|
||
|
public volatile bool multithreaded = false;
|
||
|
[HideInInspector]
|
||
|
public bool buildOnAwake = true;
|
||
|
[HideInInspector]
|
||
|
public bool buildOnEnable = false;
|
||
|
|
||
|
public event EmptySplineHandler onPostBuild;
|
||
|
|
||
|
#if UNITY_EDITOR
|
||
|
public virtual void EditorAwake()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
protected virtual void Awake() {
|
||
|
#if UNITY_EDITOR
|
||
|
_isPlaying = Application.isPlaying;
|
||
|
if (!_isPlaying)
|
||
|
{
|
||
|
if (spline != null)
|
||
|
{
|
||
|
if (!_spline.IsSubscribed(this))
|
||
|
{
|
||
|
_spline.Subscribe(this);
|
||
|
UnityEditor.EditorUtility.SetDirty(spline);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
CacheTransform();
|
||
|
if (buildOnAwake && Application.isPlaying)
|
||
|
{
|
||
|
RebuildImmediate();
|
||
|
} else
|
||
|
{
|
||
|
GetSamples();
|
||
|
}
|
||
|
|
||
|
#if UNITY_EDITOR
|
||
|
if (!Application.isPlaying)
|
||
|
{
|
||
|
RebuildImmediate();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
protected void CacheTransform()
|
||
|
{
|
||
|
_trs = transform;
|
||
|
_hasTransform = true;
|
||
|
}
|
||
|
|
||
|
protected virtual void Reset()
|
||
|
{
|
||
|
#if UNITY_EDITOR
|
||
|
spline = GetComponent<SplineComputer>();
|
||
|
Awake();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
protected virtual void OnEnable()
|
||
|
{
|
||
|
#if UNITY_EDITOR
|
||
|
if (!_isPlaying || buildOnEnable)
|
||
|
{
|
||
|
RebuildImmediate();
|
||
|
}
|
||
|
#else
|
||
|
if (buildOnEnable){
|
||
|
RebuildImmediate();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
protected virtual void OnDisable()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected virtual void OnDestroy()
|
||
|
{
|
||
|
#if UNITY_EDITOR
|
||
|
if (!_isPlaying && spline != null)
|
||
|
{
|
||
|
_spline.Unsubscribe(this); //Unsubscribe if DestroyImmediate is called
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
protected virtual void OnDidApplyAnimationProperties()
|
||
|
{
|
||
|
bool clip = false;
|
||
|
if (_clipFrom != animClipFrom || _clipTo != animClipTo) clip = true;
|
||
|
_clipFrom = animClipFrom;
|
||
|
_clipTo = animClipTo;
|
||
|
Rebuild();
|
||
|
if (clip) GetSamples();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the sample at the given index without modifications
|
||
|
/// </summary>
|
||
|
/// <param name="index">Sample index</param>
|
||
|
/// <returns></returns>
|
||
|
public void GetSampleRaw(int index, ref SplineSample sample)
|
||
|
{
|
||
|
if (index == 0)
|
||
|
{
|
||
|
sample.FastCopy(ref _clipFromSample);
|
||
|
return;
|
||
|
}
|
||
|
if (index == _sampleCount - 1)
|
||
|
{
|
||
|
sample.FastCopy(ref _clipToSample);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ClampLoopSampleIndex(ref index);
|
||
|
sample.FastCopy(ref _sampleCollection.samples[index]);
|
||
|
}
|
||
|
|
||
|
public double GetSamplePercent(int index)
|
||
|
{
|
||
|
if (index == 0)
|
||
|
{
|
||
|
return _clipFromSample.percent;
|
||
|
}
|
||
|
if (index == _sampleCount - 1)
|
||
|
{
|
||
|
return _clipToSample.percent;
|
||
|
}
|
||
|
|
||
|
ClampLoopSampleIndex(ref index);
|
||
|
return _sampleCollection.samples[index].percent;
|
||
|
}
|
||
|
|
||
|
private void ClampLoopSampleIndex(ref int index)
|
||
|
{
|
||
|
if (index >= _sampleCount)
|
||
|
{
|
||
|
index = _sampleCount - 1;
|
||
|
}
|
||
|
|
||
|
if (samplesAreLooped)
|
||
|
{
|
||
|
int start;
|
||
|
double lerp;
|
||
|
_sampleCollection.GetSamplingValues(clipFrom, out start, out lerp);
|
||
|
|
||
|
index = start + index;
|
||
|
if (index >= _sampleCollection.length)
|
||
|
{
|
||
|
index -= _sampleCollection.length;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
index = _startSampleIndex + index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the sample at the given index with modifiers applied
|
||
|
/// </summary>
|
||
|
/// <param name="index">Sample index</param>
|
||
|
/// <param name="target">Sample to write to</param>
|
||
|
public void GetSample(int index, ref SplineSample target)
|
||
|
{
|
||
|
GetSampleRaw(index, ref _workSample);
|
||
|
ModifySample(ref _workSample, ref target);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the sample at the given index with modifiers applied and
|
||
|
/// applies compensation to the size parameter based on the angle between the samples
|
||
|
/// </summary>
|
||
|
public void GetSampleWithAngleCompensation(int index, ref SplineSample target)
|
||
|
{
|
||
|
GetSampleRaw(index, ref target);
|
||
|
ModifySample(ref target, ref target);
|
||
|
if(index > 0 && index < sampleCount - 1)
|
||
|
{
|
||
|
GetSampleRaw(index - 1, ref _workSample);
|
||
|
ModifySample(ref _workSample, ref _workSample);
|
||
|
Vector3 prev = target.position - _workSample.position;
|
||
|
GetSampleRaw(index + 1, ref _workSample);
|
||
|
ModifySample(ref _workSample, ref _workSample);
|
||
|
Vector3 next = _workSample.position - target.position;
|
||
|
target.size *= 1 / Mathf.Sqrt(Vector3.Dot(prev.normalized, next.normalized) * 0.5f + 0.5f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// Rebuild the SplineUser. This will cause Build and Build_MT to be called.
|
||
|
/// </summary>
|
||
|
/// <param name="sampleComputer">Should the SplineUser sample the SplineComputer</param>
|
||
|
public virtual void Rebuild()
|
||
|
{
|
||
|
#if UNITY_EDITOR
|
||
|
if (!_hasTransform)
|
||
|
{
|
||
|
CacheTransform();
|
||
|
}
|
||
|
|
||
|
//If it's the editor and it's not playing, then rebuild immediate
|
||
|
if (_isPlaying)
|
||
|
{
|
||
|
if (!autoUpdate) return;
|
||
|
rebuild = getSamples = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RebuildImmediate();
|
||
|
}
|
||
|
#else
|
||
|
if (!autoUpdate) return;
|
||
|
rebuild = getSamples = true;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Rebuild the SplineUser immediate. This method will call sample samples and call Build as soon as it's called even if the component is disabled.
|
||
|
/// </summary>
|
||
|
/// <param name="sampleComputer">Should the SplineUser sample the SplineComputer</param>
|
||
|
public virtual void RebuildImmediate()
|
||
|
{
|
||
|
#if UNITY_EDITOR
|
||
|
if (!_hasTransform)
|
||
|
{
|
||
|
CacheTransform();
|
||
|
}
|
||
|
#endif
|
||
|
try
|
||
|
{
|
||
|
GetSamples();
|
||
|
Build();
|
||
|
PostBuild();
|
||
|
}
|
||
|
catch (System.Exception ex)
|
||
|
{
|
||
|
Debug.LogError(ex.Message);
|
||
|
}
|
||
|
rebuild = false;
|
||
|
getSamples = false;
|
||
|
}
|
||
|
|
||
|
private void Update()
|
||
|
{
|
||
|
if (updateMethod == UpdateMethod.Update)
|
||
|
{
|
||
|
Run();
|
||
|
RunUpdate();
|
||
|
LateRun();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void LateUpdate()
|
||
|
{
|
||
|
if (updateMethod == UpdateMethod.LateUpdate)
|
||
|
{
|
||
|
Run();
|
||
|
RunUpdate();
|
||
|
LateRun();
|
||
|
}
|
||
|
#if UNITY_EDITOR
|
||
|
if(!_isPlaying && updateMethod == UpdateMethod.FixedUpdate)
|
||
|
{
|
||
|
Run();
|
||
|
RunUpdate();
|
||
|
LateRun();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
private void FixedUpdate()
|
||
|
{
|
||
|
if (updateMethod == UpdateMethod.FixedUpdate)
|
||
|
{
|
||
|
Run();
|
||
|
RunUpdate();
|
||
|
LateRun();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Update logic for handling threads and rebuilding
|
||
|
private void RunUpdate()
|
||
|
{
|
||
|
#if UNITY_EDITOR
|
||
|
if (!_isPlaying) return;
|
||
|
#endif
|
||
|
//Handle rebuilding
|
||
|
if (rebuild)
|
||
|
{
|
||
|
if (multithreaded)
|
||
|
{
|
||
|
if (getSamples) SplineThreading.Run(ResampleAndBuildThreaded);
|
||
|
else SplineThreading.Run(BuildThreaded);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (getSamples || _spline.sampleMode == SplineComputer.SampleMode.Optimized) GetSamples();
|
||
|
Build();
|
||
|
postBuild = true;
|
||
|
}
|
||
|
rebuild = false;
|
||
|
}
|
||
|
if (postBuild)
|
||
|
{
|
||
|
PostBuild();
|
||
|
EmptySplineHandler postBuildHandler = onPostBuild;
|
||
|
if(postBuildHandler != null)
|
||
|
{
|
||
|
postBuildHandler();
|
||
|
}
|
||
|
postBuild = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BuildThreaded()
|
||
|
{
|
||
|
while (postBuild)
|
||
|
{
|
||
|
//Wait if the main thread is still running post build operations
|
||
|
}
|
||
|
Build();
|
||
|
postBuild = true;
|
||
|
}
|
||
|
|
||
|
private void ResampleAndBuildThreaded()
|
||
|
{
|
||
|
while (postBuild)
|
||
|
{
|
||
|
//Wait if the main thread is still running post build operations
|
||
|
}
|
||
|
GetSamples();
|
||
|
Build();
|
||
|
postBuild = true;
|
||
|
}
|
||
|
|
||
|
/// Code to run every Update/FixedUpdate/LateUpdate before any building has taken place
|
||
|
protected virtual void Run()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
/// Code to run every Update/FixedUpdate/LateUpdate after any rabuilding has taken place
|
||
|
protected virtual void LateRun()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
//Used for calculations. Called on the main or the worker thread.
|
||
|
protected virtual void Build()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//Called on the Main thread only - used for applying the results from Build
|
||
|
protected virtual void PostBuild()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected virtual void OnSplineChanged()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Applies the SplineUser modifiers to the provided sample
|
||
|
/// </summary>
|
||
|
/// <param name="source">Original sample</param>
|
||
|
/// <param name="destination">Destination sample</param>
|
||
|
public void ModifySample(ref SplineSample source, ref SplineSample destination)
|
||
|
{
|
||
|
destination = source;
|
||
|
ModifySample(ref destination);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Applies the SplineUser modifiers to the provided sample
|
||
|
/// </summary>
|
||
|
/// <param name="sample"></param>
|
||
|
public void ModifySample(ref SplineSample sample)
|
||
|
{
|
||
|
ApplyModifier(_offsetModifier, ref sample);
|
||
|
ApplyModifier(_rotationModifier, ref sample);
|
||
|
ApplyModifier(_colorModifier, ref sample);
|
||
|
ApplyModifier(_sizeModifier, ref sample);
|
||
|
}
|
||
|
|
||
|
private void ApplyModifier(SplineSampleModifier modifier, ref SplineSample sample)
|
||
|
{
|
||
|
if (modifier.useClippedPercent)
|
||
|
{
|
||
|
ClipPercent(ref sample.percent);
|
||
|
}
|
||
|
modifier.Apply(ref sample);
|
||
|
if (modifier.useClippedPercent)
|
||
|
{
|
||
|
UnclipPercent(ref sample.percent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the clip range of the SplineUser. Same as setting clipFrom and clipTo
|
||
|
/// </summary>
|
||
|
/// <param name="from"></param>
|
||
|
/// <param name="to"></param>
|
||
|
public void SetClipRange(double from, double to)
|
||
|
{
|
||
|
if (!_spline.isClosed && to < from) to = from;
|
||
|
_clipFrom = DMath.Clamp01(from);
|
||
|
_clipTo = DMath.Clamp01(to);
|
||
|
GetSamples();
|
||
|
Rebuild();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the clipped samples defined by clipFrom and clipTo
|
||
|
/// </summary>
|
||
|
private void GetSamples()
|
||
|
{
|
||
|
getSamples = false;
|
||
|
if (spline == null)
|
||
|
{
|
||
|
_sampleCollection.samples = new SplineSample[0];
|
||
|
_sampleCount = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_spline.GetSamples(_sampleCollection);
|
||
|
if(_clipFrom != 0.0)
|
||
|
{
|
||
|
_sampleCollection.Evaluate(clipFrom, ref _clipFromSample);
|
||
|
} else
|
||
|
{
|
||
|
_clipFromSample = _sampleCollection.samples[0];
|
||
|
}
|
||
|
|
||
|
if(_clipTo != 1.0)
|
||
|
{
|
||
|
_sampleCollection.Evaluate(_clipTo, ref _clipToSample);
|
||
|
} else
|
||
|
{
|
||
|
_clipToSample = _sampleCollection.samples[_sampleCollection.length - 1];
|
||
|
}
|
||
|
|
||
|
int start, end;
|
||
|
_sampleCount = _sampleCollection.GetClippedSampleCount(_clipFrom, _clipTo, out start, out end);
|
||
|
double lerp;
|
||
|
_sampleCollection.GetSamplingValues(_clipFrom, out _startSampleIndex, out lerp);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Takes a regular 0-1 percent mapped to the start and end of the spline and maps it to the clipFrom and clipTo valies. Useful for working with clipped samples
|
||
|
/// </summary>
|
||
|
/// <param name="percent"></param>
|
||
|
/// <returns></returns>
|
||
|
public double ClipPercent(double percent)
|
||
|
{
|
||
|
ClipPercent(ref percent);
|
||
|
return percent;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Takes a regular 0-1 percent mapped to the start and end of the spline and maps it to the clipFrom and clipTo valies. Useful for working with clipped samples
|
||
|
/// </summary>
|
||
|
/// <param name="percent"></param>
|
||
|
/// <returns></returns>
|
||
|
public void ClipPercent(ref double percent)
|
||
|
{
|
||
|
if (_sampleCollection.length == 0)
|
||
|
{
|
||
|
percent = 0.0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (samplesAreLooped)
|
||
|
{
|
||
|
if (percent >= clipFrom && percent <= 1.0) { percent = DMath.InverseLerp(clipFrom, clipFrom + span, percent); }//If in the range clipFrom - 1.0
|
||
|
else if (percent <= clipTo) { percent = DMath.InverseLerp(clipTo - span, clipTo, percent); } //if in the range 0.0 - clipTo
|
||
|
else
|
||
|
{
|
||
|
//Find the nearest clip start
|
||
|
if (DMath.InverseLerp(clipTo, clipFrom, percent) < 0.5) percent = 1.0;
|
||
|
else percent = 0.0;
|
||
|
}
|
||
|
}
|
||
|
else percent = DMath.InverseLerp(clipFrom, clipTo, percent);
|
||
|
}
|
||
|
|
||
|
public double UnclipPercent(double percent)
|
||
|
{
|
||
|
UnclipPercent(ref percent);
|
||
|
return percent;
|
||
|
}
|
||
|
|
||
|
public void UnclipPercent(ref double percent)
|
||
|
{
|
||
|
if (percent == 0.0)
|
||
|
{
|
||
|
percent = clipFrom;
|
||
|
return;
|
||
|
}
|
||
|
else if (percent == 1.0)
|
||
|
{
|
||
|
percent = clipTo;
|
||
|
return;
|
||
|
}
|
||
|
if (samplesAreLooped)
|
||
|
{
|
||
|
double fromLength = (1.0 - clipFrom) / span;
|
||
|
if (fromLength == 0.0)
|
||
|
{
|
||
|
percent = 0.0;
|
||
|
return;
|
||
|
}
|
||
|
if (percent < fromLength) percent = DMath.Lerp(clipFrom, 1.0, percent / fromLength);
|
||
|
else if (clipTo == 0.0)
|
||
|
{
|
||
|
percent = 0.0;
|
||
|
return;
|
||
|
}
|
||
|
else percent = DMath.Lerp(0.0, clipTo, (percent - fromLength) / (clipTo / span));
|
||
|
}
|
||
|
else percent = DMath.Lerp(clipFrom, clipTo, percent);
|
||
|
percent = DMath.Clamp01(percent);
|
||
|
}
|
||
|
|
||
|
private int GetSampleIndex(double percent)
|
||
|
{
|
||
|
int index;
|
||
|
double lerp;
|
||
|
_sampleCollection.GetSamplingValues(UnclipPercent(percent), out index, out lerp);
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
public Vector3 EvaluatePosition(double percent)
|
||
|
{
|
||
|
return _sampleCollection.EvaluatePosition(UnclipPercent(percent));
|
||
|
}
|
||
|
|
||
|
public void Evaluate(double percent, ref SplineSample result)
|
||
|
{
|
||
|
_sampleCollection.Evaluate(UnclipPercent(percent), ref result);
|
||
|
result.percent = DMath.Clamp01(percent);
|
||
|
}
|
||
|
|
||
|
public SplineSample Evaluate(double percent)
|
||
|
{
|
||
|
SplineSample result = new SplineSample();
|
||
|
Evaluate(percent, ref result);
|
||
|
result.percent = DMath.Clamp01(percent);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public void Evaluate(ref SplineSample[] results, double from = 0.0, double to = 1.0)
|
||
|
{
|
||
|
_sampleCollection.Evaluate(ref results, UnclipPercent(from), UnclipPercent(to));
|
||
|
for (int i = 0; i < results.Length; i++)
|
||
|
{
|
||
|
ClipPercent(ref results[i].percent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void EvaluatePositions(ref Vector3[] positions, double from = 0.0, double to = 1.0)
|
||
|
{
|
||
|
_sampleCollection.EvaluatePositions(ref positions, UnclipPercent(from), UnclipPercent(to));
|
||
|
}
|
||
|
|
||
|
public double Travel(double start, float distance, Spline.Direction direction, out float moved)
|
||
|
{
|
||
|
moved = 0f;
|
||
|
if (direction == Spline.Direction.Forward && start >= 1.0)
|
||
|
{
|
||
|
return 1.0;
|
||
|
}
|
||
|
else if (direction == Spline.Direction.Backward && start <= 0.0)
|
||
|
{
|
||
|
return 0.0;
|
||
|
}
|
||
|
if (distance == 0f)
|
||
|
{
|
||
|
return DMath.Clamp01(start);
|
||
|
}
|
||
|
double result = _sampleCollection.Travel(UnclipPercent(start), distance, direction, out moved, clipFrom, clipTo);
|
||
|
return ClipPercent(result);
|
||
|
}
|
||
|
|
||
|
public double Travel(double start, float distance, Spline.Direction direction = Spline.Direction.Forward)
|
||
|
{
|
||
|
float moved;
|
||
|
return Travel(start, distance, direction, out moved);
|
||
|
}
|
||
|
|
||
|
public double TravelWithOffset(double start, float distance, Spline.Direction direction, Vector3 offset, out float moved)
|
||
|
{
|
||
|
moved = 0f;
|
||
|
if (direction == Spline.Direction.Forward && start >= 1.0)
|
||
|
{
|
||
|
return 1.0;
|
||
|
}
|
||
|
else if (direction == Spline.Direction.Backward && start <= 0.0)
|
||
|
{
|
||
|
return 0.0;
|
||
|
}
|
||
|
if (distance == 0f)
|
||
|
{
|
||
|
return DMath.Clamp01(start);
|
||
|
}
|
||
|
double result = _sampleCollection.TravelWithOffset(UnclipPercent(start), distance, direction, offset, out moved, clipFrom, clipTo);
|
||
|
return ClipPercent(result);
|
||
|
}
|
||
|
|
||
|
public virtual void Project(Vector3 position, ref SplineSample result, double from = 0.0, double to = 1.0)
|
||
|
{
|
||
|
if (_spline == null) return;
|
||
|
_sampleCollection.Project(position, _spline.pointCount, ref result, UnclipPercent(from), UnclipPercent(to));
|
||
|
ClipPercent(ref result.percent);
|
||
|
}
|
||
|
|
||
|
public float CalculateLength(double from = 0.0, double to = 1.0)
|
||
|
{
|
||
|
return _sampleCollection.CalculateLength(UnclipPercent(from), UnclipPercent(to));
|
||
|
}
|
||
|
|
||
|
public float CalculateLengthWithOffset(Vector3 offset, double from = 0.0, double to = 1.0)
|
||
|
{
|
||
|
return _sampleCollection.CalculateLengthWithOffset(offset, UnclipPercent(from), UnclipPercent(to));
|
||
|
}
|
||
|
|
||
|
public virtual void OnBeforeSerialize()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public virtual void OnAfterDeserialize()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the offset transformed by the sample
|
||
|
/// </summary>
|
||
|
/// <param name="sample">Source sample</param>
|
||
|
/// <param name="localOffset">Local offset to apply</param>
|
||
|
/// <returns></returns>
|
||
|
protected static Vector3 TransformOffset(SplineSample sample, Vector3 localOffset)
|
||
|
{
|
||
|
return (sample.right * localOffset.x + sample.up * localOffset.y + sample.forward * localOffset.z) * sample.size;
|
||
|
}
|
||
|
}
|
||
|
}
|