using UnityEngine; using System.Collections; using System.Collections.Generic; using System; namespace uc.Spline { public enum UniSplineType { CatmullRom, Hermite, KochanekBartels } public enum SplineWrapMode { Once, Repeat, PingPong, Loop } public enum SplinePointType { Corner, Smooth, Bezier, BezierCorner }; public enum SplineSegmentType { Linear, Curve }; public enum SplineReparamType { None, Simple, RungeKutta }; [Serializable] public class BaseSplinePoint { public BaseSplinePoint(Vector3 m_point) { this.m_point = m_point; } public Vector3 m_point; } [Serializable] public class BaseSplineSegment { public Vector3 m_startpos, m_startctrl, m_endctrl, m_endpos; public float m_startlen, m_endlen, m_length; public float[] m_params, m_precomps; internal virtual Vector3 GetPosition(float t) { return Vector3.zero; } internal virtual Vector3 GetTangent(float t) { return Vector3.zero; } internal virtual Vector3 GetNormal(float t) { return Vector3.zero; } } public abstract class BaseSpline { public class SplineIterator { internal SplineIterator(BaseSpline spline, bool reverse, int startidx, int endidx) { m_spline = spline; m_reverse = reverse; m_startidx = Mathf.Min(startidx, endidx); m_endidx = Mathf.Max(startidx, endidx); Reset(); } public void SetTransform(Transform trnsfrm) { m_transform = trnsfrm; } public Vector3 GetPosition() { if (m_transform != null) { return m_transform.TransformPoint(m_spline.GetPosition(m_segidx, m_segpos)); } else { return m_spline.GetPosition(m_segidx, m_segpos); } } public Vector3 GetTangent() { Quaternion rotation = m_transform != null ? m_transform.localRotation : Quaternion.identity; float direction = m_reverse ? -1 : 1; return rotation * (direction * m_spline.GetTangent(m_segidx, m_segpos)); } public Vector3 GetNormal() { Quaternion rotation = m_transform != null ? m_transform.localRotation : Quaternion.identity; return rotation * m_spline.GetNormal(m_segidx, m_segpos); } public bool IsOnceOut() { return m_onceout; } public void Reset() { if (m_reverse) { SetToEnd(); } else { SetToStart(); } m_back = false; m_onceout = false; } public void SetOffset(float offset) { offset = WrapPosition(m_spline.WrapMode, offset, m_spline.Length); m_segidx = m_spline.FindSegment(offset); m_segpos = offset - m_spline.GetSegmentStartLength(m_segidx); } public float GetOffset() { return m_spline.GetSegmentStartLength(m_segidx) + m_segpos; } public void SetOffsetPercent(float offset) { offset = WrapPosition(m_spline.WrapMode, offset, m_spline.Length); m_segidx = m_spline.FindSegment(offset * m_spline.Length); m_segpos = offset - m_spline.GetSegmentStartLength(m_segidx); } public float GetOffsetPercent() { float result = (m_spline.GetSegmentStartLength(m_segidx) + m_segpos) / m_spline.Length; if (m_reverse) result = 1 - result; return result; } public void Iterate(float length) { bool stop = false, back = false; if (m_reverse) { length = -length; } if (length < 0) { back = !m_back; length = -length; } else { back = m_back; } if (back) { while (m_segpos - length < 0 && !stop) { length -= m_segpos; if (m_segidx - 1 < m_startidx) { switch (m_spline.WrapMode) { case SplineWrapMode.Loop: SetToEnd(); break; case SplineWrapMode.Once: SetToStart(); stop = true; m_onceout = true; break; case SplineWrapMode.Repeat: SetToEnd(); break; case SplineWrapMode.PingPong: SetToStart(); m_back = !m_back; stop = true; break; } } else { --m_segidx; m_segpos = m_spline.GetSegmentLength(m_segidx); } } if (!stop) { m_segpos -= length; } } else { while (m_segpos + length > m_spline.GetSegmentLength(m_segidx) && !stop) { length -= m_spline.GetSegmentLength(m_segidx) - m_segpos; if (m_segidx + 1 >= m_endidx) { switch (m_spline.m_wrapmode) { case SplineWrapMode.Loop: SetToStart(); break; case SplineWrapMode.Once: SetToEnd(); stop = true; m_onceout = true; break; case SplineWrapMode.Repeat: SetToStart(); break; case SplineWrapMode.PingPong: SetToEnd(); m_back = !m_back; stop = true; break; } } else { ++m_segidx; m_segpos = 0; } } if (!stop) { m_segpos += length; } } } private void SetToStart() { m_segidx = m_startidx; m_segpos = 0; } private void SetToEnd() { m_segidx = m_endidx - 1; m_segpos = m_spline.GetSegmentLength(m_segidx); } private Transform m_transform = null; private BaseSpline m_spline; private int m_segidx = 0, m_startidx = 0, m_endidx = 0; private bool m_reverse = false, m_back = false, m_onceout = false; private float m_segpos = 0; } public abstract class BaseEditHelper { protected BaseSpline m_spline; protected int m_idx = -1, m_selidx = -1; internal BaseEditHelper(BaseSpline spline) { m_spline = spline; } public virtual bool MoveNext() { ++m_idx; if (m_idx < m_spline.PointCount) { return true; } return false; } public virtual void Reset() { m_idx = -1; } public abstract void AppendPoint(); public abstract void InsertBefore(); public abstract void InsertAfter(); public abstract void Remove(); public virtual void RemoveLast() { if (m_spline.PointCount > 0) { RemoveLastPoint(); } if (m_selidx >= m_spline.PointCount) { m_selidx = m_spline.PointCount - 1; } } public virtual void SelectFirst() { m_selidx = (m_spline.PointCount > 0) ? 0 : -1; } public virtual void SelectNext() { m_selidx = (m_selidx < m_spline.PointCount - 1) ? m_selidx + 1 : 0; } public virtual void SelectPrev() { m_selidx = (m_selidx > 0) ? m_selidx - 1 : m_spline.PointCount - 1; } public virtual int Index { get { return m_idx; } } public virtual int SelectedIndex { get { return m_selidx; } } public virtual bool Selected { get { return m_idx == m_selidx; } set { if (value) { m_selidx = m_idx; } else { m_selidx = -1; } } } public virtual bool SomethingSelected { get { return m_selidx != -1; } } public abstract BaseSplinePoint Point { get; set; } public abstract BaseSplinePoint SelectedPoint { get; set; } public abstract void RemoveLastPoint(); public abstract void RemoveAllPoints(); } [SerializeField] protected SplineWrapMode m_wrapmode = SplineWrapMode.Once; [SerializeField] protected float m_length = 0; [SerializeField] protected int m_stepcount = 8; [SerializeField] protected SplineReparamType m_reparam = SplineReparamType.None; protected int m_buildnum = 0; [SerializeField] protected float m_precompdiv = 1; public int BuildNum { get { return m_buildnum; } } public abstract int PointCount { get; } public float Length { get { return m_length; } } public SplineWrapMode WrapMode { get { return m_wrapmode; } set { m_wrapmode = value; } } public int StepCount { get { return m_stepcount; } set { if (value > 1) { m_stepcount = value; } } } public SplineReparamType ReparamType { get { return m_reparam; } set { m_reparam = value; } } public abstract void Build(); public abstract int GetPointCount(); public abstract int GetSegmentCount(); protected abstract float GetSegmentLength(int segidx); protected abstract float GetSegmentStartLength(int segidx); protected abstract float GetSegmentEndLength(int segidx); protected abstract int FindSegment(float offset); protected abstract Vector3 GetDrawPosition(int segidx, float segpos); protected abstract Vector3 GetPosition(int segidx, float segpos); protected abstract Vector3 GetTangent(int segidx, float segpos); protected abstract Vector3 GetNormal(int segidx, float segpos); internal abstract void AppendPoint(BaseSplinePoint p); internal abstract void InsertPoint(int idx, BaseSplinePoint p); internal abstract void RemovePoint(int idx); internal abstract Vector3 GetPointPosition(int idx); public SplineIterator GetIterator() { return new SplineIterator(this, false, 0, GetSegmentCount()); } public SplineIterator GetReverseIterator() { return new SplineIterator(this, true, 0, GetSegmentCount()); } public SplineIterator GetPartialIterator(int startidx, int endidx) { return new SplineIterator(this, false, startidx, endidx); } public SplineIterator GetPartialReverseIterator(int startidx, int endidx) { return new SplineIterator(this, true, startidx, endidx); } public Vector3[] GenerateDrawPoints(int divs) { int segcnt = GetSegmentCount(), ptidx = 0; Vector3[] dc = new Vector3[segcnt * divs + 1]; float dt = 1 / (float)divs; dc[ptidx] = GetDrawPosition(0, 0); ++ptidx; for (int i = 0; i < segcnt; ++i) { for (int j = 1; j < divs + 1; ++j) { dc[ptidx] = GetDrawPosition(i, (float)j * dt); ++ptidx; } } return dc; } public abstract BaseEditHelper GetEditHelper(); static public float WrapPosition(SplineWrapMode wrapmode, float pos, float len) { switch (wrapmode) { case SplineWrapMode.Loop: case SplineWrapMode.Repeat: pos = Mathf.Repeat(pos, len); break; case SplineWrapMode.PingPong: pos = Mathf.PingPong(pos, len); break; case SplineWrapMode.Once: pos = Mathf.Clamp(pos, 0, len); break; } return pos; } protected float GetReparamRungeKutta(BaseSplineSegment ss, float u) { float t = 0, k1, k2, k3, k4, h = u / (float)m_stepcount, mag; for (int i = 1; i <= m_stepcount; i++) { mag = ss.GetTangent(t).magnitude; if (mag == 0) { k1 = 0; k2 = 0; k3 = 0; k4 = 0; } else { k1 = h / ss.GetTangent(t).magnitude; k2 = h / ss.GetTangent(t + k1 * 0.5f).magnitude; k3 = h / ss.GetTangent(t + k2 * 0.5f).magnitude; k4 = h / ss.GetTangent(t + k3).magnitude; } t += (k1 + 2 * (k2 + k3) + k4) * 0.16666666666666666666666666666667f; } return t; } protected float GetReparam(BaseSplineSegment ss, float u) { if (u <= 0) { return 0; } else if (u >= 1) { return 1; } switch (m_reparam) { case SplineReparamType.RungeKutta: { int ridx = (int)(u * (float)m_stepcount); float uc = (u - ss.m_precomps[ridx]) / m_precompdiv; return Mathf.Lerp(ss.m_params[ridx], ss.m_params[ridx + 1], uc); } case SplineReparamType.Simple: { int ridx = 0; for (int i = 1; i < m_stepcount + 1; ++i) { if (ss.m_precomps[i] > u) { ridx = i - 1; break; } } float uc = (u - ss.m_precomps[ridx]) / (ss.m_precomps[ridx + 1] - ss.m_precomps[ridx]); return Mathf.Lerp(ss.m_params[ridx], ss.m_params[ridx + 1], uc); } default: return 0; } } } }