323 lines
11 KiB
C#
323 lines
11 KiB
C#
namespace Dreamteck.Splines
|
|
{
|
|
using UnityEngine;
|
|
[AddComponentMenu("Dreamteck/Splines/Morph")]
|
|
public class SplineMorph : MonoBehaviour
|
|
{
|
|
[HideInInspector]
|
|
public SplineComputer.Space space = SplineComputer.Space.Local;
|
|
[HideInInspector]
|
|
public bool cycle = false;
|
|
public enum CycleMode {Default, Loop, PingPong}
|
|
public enum UpdateMode {Update, FixedUpdate, LateUpdate}
|
|
[HideInInspector]
|
|
public CycleMode cycleMode = CycleMode.Default;
|
|
[HideInInspector]
|
|
public UpdateMode cycleUpdateMode = UpdateMode.Update;
|
|
[HideInInspector]
|
|
public float cycleDuration = 1f;
|
|
public SplineComputer spline
|
|
{
|
|
get { return _spline; }
|
|
set
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
if(channels.Length > 0 && value.pointCount != channels[0].points.Length)
|
|
{
|
|
value.SetPoints(channels[0].points, space);
|
|
}
|
|
}
|
|
_spline = value;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
private SplineComputer _spline;
|
|
private SplinePoint[] points = new SplinePoint[0];
|
|
private float cycleValue = 0f;
|
|
private short cycleDirection = 1;
|
|
|
|
|
|
[System.Serializable]
|
|
public class Channel
|
|
{
|
|
public enum Interpolation { Linear, Spherical }
|
|
[SerializeField]
|
|
internal SplinePoint[] points = new SplinePoint[0];
|
|
[SerializeField]
|
|
internal float percent = 1f;
|
|
public string name = "";
|
|
public AnimationCurve curve;
|
|
public Interpolation interpolation = Interpolation.Linear;
|
|
}
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
[UnityEngine.Serialization.FormerlySerializedAs("morphStates")]
|
|
private Channel[] channels = new Channel[0];
|
|
|
|
private void Reset()
|
|
{
|
|
spline = GetComponent<SplineComputer>();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (cycleUpdateMode == UpdateMode.Update) RunUpdate();
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (cycleUpdateMode == UpdateMode.FixedUpdate) RunUpdate();
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
if (cycleUpdateMode == UpdateMode.LateUpdate) RunUpdate();
|
|
}
|
|
|
|
void RunUpdate()
|
|
{
|
|
if (!cycle) return;
|
|
if (cycleMode != CycleMode.PingPong) cycleDirection = 1;
|
|
cycleValue += Time.deltaTime / cycleDuration * cycleDirection;
|
|
switch (cycleMode)
|
|
{
|
|
case CycleMode.Default:
|
|
if (cycleValue > 1f) cycleValue = 1f;
|
|
break;
|
|
case CycleMode.Loop:
|
|
if (cycleValue > 1f) cycleValue -= Mathf.Floor(cycleValue);
|
|
break;
|
|
case CycleMode.PingPong:
|
|
if (cycleValue > 1f)
|
|
{
|
|
cycleValue = 1f - (cycleValue - Mathf.Floor(cycleValue));
|
|
cycleDirection = -1;
|
|
} else if (cycleValue < 0f)
|
|
{
|
|
cycleValue = -cycleValue - Mathf.Floor(-cycleValue);
|
|
cycleDirection = 1;
|
|
}
|
|
break;
|
|
}
|
|
SetWeight(cycleValue, cycleMode == CycleMode.Loop);
|
|
}
|
|
|
|
public void SetCycle(float value)
|
|
{
|
|
cycleValue = Mathf.Clamp01(value);
|
|
}
|
|
|
|
public void SetWeight(int index, float weight)
|
|
{
|
|
channels[index].percent = Mathf.Clamp01(weight);
|
|
UpdateMorph();
|
|
}
|
|
|
|
public void SetWeight(string name, float weight)
|
|
{
|
|
int index = GetChannelIndex(name);
|
|
channels[index].percent = Mathf.Clamp01(weight);
|
|
UpdateMorph();
|
|
}
|
|
|
|
public void SetWeight(float percent, bool loop = false)
|
|
{
|
|
float channelValue = percent * (loop ? channels.Length : channels.Length - 1);
|
|
for (int i = 0; i < channels.Length; i++)
|
|
{
|
|
float delta = Mathf.Abs(i - channelValue);
|
|
if (delta > 1f)
|
|
{
|
|
SetWeight(i, 0f);
|
|
}
|
|
else
|
|
{
|
|
if (channelValue <= i)
|
|
{
|
|
SetWeight(i, 1f - (i - channelValue));
|
|
}
|
|
else
|
|
{
|
|
SetWeight(i, 1f - (channelValue - i));
|
|
}
|
|
}
|
|
}
|
|
if (loop && channelValue >= channels.Length - 1)
|
|
{
|
|
SetWeight(0, channelValue - (channels.Length - 1));
|
|
}
|
|
}
|
|
|
|
public void CaptureSnapshot(string name)
|
|
{
|
|
CaptureSnapshot(GetChannelIndex(name));
|
|
}
|
|
|
|
public void CaptureSnapshot(int index)
|
|
{
|
|
if (_spline == null) return;
|
|
if ((channels.Length > 0 && _spline.pointCount != channels[0].points.Length && index != 0))
|
|
{
|
|
Debug.LogError("Point count must be the same as " + _spline.pointCount);
|
|
return;
|
|
}
|
|
channels[index].points = _spline.GetPoints(space);
|
|
UpdateMorph();
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
channels = new Channel[0];
|
|
}
|
|
|
|
public SplinePoint[] GetSnapshot(int index)
|
|
{
|
|
return channels[index].points;
|
|
}
|
|
public void SetSnapshot(int index, SplinePoint[] points)
|
|
{
|
|
channels[index].points = points;
|
|
}
|
|
|
|
|
|
public SplinePoint[] GetSnapshot(string name)
|
|
{
|
|
int index = GetChannelIndex(name);
|
|
return channels[index].points;
|
|
}
|
|
|
|
public float GetWeight(int index)
|
|
{
|
|
return channels[index].percent;
|
|
}
|
|
|
|
public float GetWeight(string name)
|
|
{
|
|
int index = GetChannelIndex(name);
|
|
return channels[index].percent;
|
|
}
|
|
|
|
public void AddChannel(string name)
|
|
{
|
|
if (_spline == null) return;
|
|
if (channels.Length > 0 && _spline.pointCount != channels[0].points.Length)
|
|
{
|
|
Debug.LogError("Point count must be the same as " + channels[0].points.Length);
|
|
return;
|
|
}
|
|
Channel newMorph = new Channel();
|
|
newMorph.points = _spline.GetPoints(space);
|
|
newMorph.name = name;
|
|
newMorph.curve = new AnimationCurve();
|
|
newMorph.curve.AddKey(new Keyframe(0, 0, 0, 1));
|
|
newMorph.curve.AddKey(new Keyframe(1, 1, 1, 0));
|
|
ArrayUtility.Add(ref channels, newMorph);
|
|
UpdateMorph();
|
|
}
|
|
|
|
public void RemoveChannel(string name)
|
|
{
|
|
int index = GetChannelIndex(name);
|
|
RemoveChannel(index);
|
|
}
|
|
|
|
public void RemoveChannel(int index)
|
|
{
|
|
if (index < 0 || index >= channels.Length) return;
|
|
Channel[] newStates = new Channel[channels.Length - 1];
|
|
for (int i = 0; i < channels.Length; i++)
|
|
{
|
|
if (i == index) continue;
|
|
else if (i < index) newStates[i] = channels[i];
|
|
else if (i >= index) newStates[i - 1] = channels[i];
|
|
}
|
|
channels = newStates;
|
|
UpdateMorph();
|
|
}
|
|
|
|
private int GetChannelIndex(string name)
|
|
{
|
|
for (int i = 0; i < channels.Length; i++)
|
|
{
|
|
if (channels[i].name == name)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
Debug.Log("Channel not found " + name);
|
|
return 0;
|
|
}
|
|
|
|
public int GetChannelCount()
|
|
{
|
|
if (channels == null) return 0;
|
|
return channels.Length;
|
|
}
|
|
|
|
public Channel GetChannel(int index)
|
|
{
|
|
return channels[index];
|
|
}
|
|
|
|
public Channel GetChannel(string name)
|
|
{
|
|
return channels[GetChannelIndex(name)];
|
|
}
|
|
|
|
public void UpdateMorph()
|
|
{
|
|
if (_spline == null) return;
|
|
if (channels.Length == 0) return;
|
|
if(points.Length != channels[0].points.Length)
|
|
{
|
|
points = new SplinePoint[channels[0].points.Length];
|
|
}
|
|
|
|
for (int i = 0; i < channels.Length; i++)
|
|
{
|
|
for (int j = 0; j < points.Length; j++)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
points[j] = channels[0].points[j];
|
|
continue;
|
|
}
|
|
|
|
float percent = channels[i].curve.Evaluate(channels[i].percent);
|
|
if (channels[i].interpolation == Channel.Interpolation.Linear)
|
|
{
|
|
points[j].position += (channels[i].points[j].position - channels[0].points[j].position) * percent;
|
|
points[j].tangent += (channels[i].points[j].tangent - channels[0].points[j].tangent) * percent;
|
|
points[j].tangent2 += (channels[i].points[j].tangent2 - channels[0].points[j].tangent2) * percent;
|
|
points[j].normal += (channels[i].points[j].normal - channels[0].points[j].normal) * percent;
|
|
} else
|
|
{
|
|
points[j].position = Vector3.Slerp(points[j].position, points[j].position + (channels[i].points[j].position - channels[0].points[j].position), percent);
|
|
points[j].tangent = Vector3.Slerp(points[j].tangent, points[j].tangent + (channels[i].points[j].tangent - channels[0].points[j].tangent), percent);
|
|
points[j].tangent2 = Vector3.Slerp(points[j].tangent2, points[j].tangent2 + (channels[i].points[j].tangent2 - channels[0].points[j].tangent2), percent);
|
|
points[j].normal = Vector3.Slerp(points[j].normal, points[j].normal + (channels[i].points[j].normal - channels[0].points[j].normal), percent);
|
|
}
|
|
|
|
points[j].color += (channels[i].points[j].color - channels[0].points[j].color) * percent;
|
|
points[j].size += (channels[i].points[j].size - channels[0].points[j].size) * percent;
|
|
|
|
if(points[j].type == SplinePoint.Type.SmoothMirrored) points[j].type = channels[i].points[j].type;
|
|
else if(points[j].type == SplinePoint.Type.SmoothFree)
|
|
{
|
|
if (channels[i].points[j].type == SplinePoint.Type.Broken) points[j].type = SplinePoint.Type.Broken;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < points.Length; i++)
|
|
{
|
|
points[i].normal.Normalize();
|
|
}
|
|
_spline.SetPoints(points, space);
|
|
}
|
|
}
|
|
}
|