You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
535 lines
16 KiB
535 lines
16 KiB
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;
|
|
}
|
|
}
|
|
|
|
}
|
|
} |