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

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;
}
}
}
}