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.

503 lines
18 KiB

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace uc.Spline
{
[Serializable]
public class UniSpline : BaseSpline
{
public class EditHelper : BaseEditHelper
{
protected new UniSpline m_spline;
internal EditHelper(UniSpline spline) : base(spline) { this.m_spline = spline; }
public override void AppendPoint()
{
Vector3 newPos = Vector3.zero;
if (m_spline.PointCount == 1)
{
newPos = m_spline.GetPointPosition(m_spline.m_points.Count - 1)+ Vector3.right;
}
else if (m_spline.PointCount > 1)
{
newPos = m_spline.GetPointPosition(m_spline.m_points.Count - 1) * 2 - m_spline.GetPointPosition(m_spline.m_points.Count - 2);
}
BaseSplinePoint p = new BaseSplinePoint(newPos);
m_spline.AppendPoint(p);
m_selidx = m_spline.PointCount - 1;
}
public override void InsertBefore()
{
if (m_spline.PointCount == 1)
{
m_spline.InsertPoint(0, new BaseSplinePoint(m_spline.GetPointPosition(m_spline.PointCount - 1)+ Vector3.left));
}
else
{
int previdx = m_selidx;
m_selidx = (int)Mathf.Repeat(m_selidx - 1, m_spline.PointCount - 1);
m_spline.InsertPoint(previdx, new BaseSplinePoint((m_spline.GetPointPosition(m_selidx) + m_spline.GetPointPosition(previdx)) * 0.5f));
m_selidx = previdx;
}
}
public override void InsertAfter()
{
if (m_spline.PointCount == 1)
{
AppendPoint();
}
else
{
int previdx = m_selidx;
m_selidx = (int)Mathf.Repeat(m_selidx + 1, m_spline.PointCount - 1);
m_spline.InsertPoint(m_selidx, new BaseSplinePoint((m_spline.GetPointPosition(m_selidx) + m_spline.GetPointPosition(previdx)) * 0.5f));
}
}
public override void Remove()
{
m_spline.m_points.RemoveAt(m_selidx);
m_selidx = (int)Mathf.Repeat(m_selidx, m_spline.PointCount - 1);
}
public override BaseSplinePoint Point
{
get { return m_spline.m_points[m_idx]; }
set { m_spline.m_points[m_idx] = value; }
}
public override BaseSplinePoint SelectedPoint
{
get { return m_spline.m_points[m_selidx]; }
set { m_spline.m_points[m_selidx] = value; }
}
public override void RemoveLastPoint()
{
m_spline.m_points.RemoveAt(m_spline.PointCount - 1);
}
public override void RemoveAllPoints()
{
m_spline.m_points.Clear();
}
}
[Serializable]
public class UniSplineSegment : BaseSplineSegment
{
public UniSplineType m_type = UniSplineType.CatmullRom;
internal override Vector3 GetPosition(float t)
{
switch (m_type)
{
case UniSplineType.CatmullRom:
return uc.Math.Spline.CatmullRom.Position(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
case UniSplineType.Hermite:
return uc.Math.Spline.Hermite.Position(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
case UniSplineType.KochanekBartels:
return uc.Math.Spline.KochanekBartels.Position(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
default: return Vector3.zero;
}
}
internal override Vector3 GetTangent(float t)
{
switch (m_type)
{
case UniSplineType.CatmullRom:
return uc.Math.Spline.CatmullRom.Tangent(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
case UniSplineType.Hermite:
return uc.Math.Spline.Hermite.Tangent(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
case UniSplineType.KochanekBartels:
return uc.Math.Spline.KochanekBartels.Tangent(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
default: return Vector3.zero;
}
}
internal override Vector3 GetNormal(float t)
{
switch (m_type)
{
case UniSplineType.CatmullRom:
return uc.Math.Spline.CatmullRom.Normal(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
case UniSplineType.Hermite:
return uc.Math.Spline.Hermite.Normal(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
case UniSplineType.KochanekBartels:
return uc.Math.Spline.KochanekBartels.Normal(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
default: return Vector3.zero;
}
}
}
[SerializeField]
private List<BaseSplinePoint> m_points = new List<BaseSplinePoint>();
[SerializeField]
private UniSplineSegment[] m_segments = null;
public override int PointCount { get { return m_points.Count; } }
[SerializeField]
private UniSplineType m_type = UniSplineType.CatmullRom;
[SerializeField]
private float m_bias = 0, m_tension = 0, m_continuity = 0;
public UniSplineType SplineType
{
get { return m_type; }
set { m_type = value; }
}
public float Bias
{
get { return m_bias; }
set { m_bias = Mathf.Clamp(value, -1, 1); }
}
public float Continuity
{
get { return m_continuity; }
set { m_continuity = Mathf.Clamp(value, -1, 1); }
}
public float Tension
{
get { return m_tension; }
set { m_tension = Mathf.Clamp(value, -1, 1); }
}
public void ReversePoints()
{
m_points.Reverse();
}
public override void Build()
{
int idx, count;
Vector3 spt1 = Vector3.zero, spt2 = Vector3.zero, spt3 = Vector3.zero, spt4 = Vector3.zero;
//
if (m_points.Count < 2)
{
m_segments = null;
m_length = 0;
return;
}
if (m_wrapmode == SplineWrapMode.Loop)
{
count = m_points.Count;
}
else
{
count = m_points.Count - 1;
}
m_segments = new UniSplineSegment[count];
m_length = 0;
idx = 0;
if (m_wrapmode == SplineWrapMode.Loop)
{
while (idx < count)
{
spt1 = m_points[(int)Mathf.Repeat(idx, m_points.Count)].m_point;
spt2 = m_points[(int)Mathf.Repeat(idx - 1, m_points.Count)].m_point;
spt3 = m_points[(int)Mathf.Repeat(idx + 2, m_points.Count)].m_point;
spt4 = m_points[(int)Mathf.Repeat(idx + 1, m_points.Count)].m_point;
m_segments[idx] = new UniSplineSegment();
(m_segments[idx] as UniSplineSegment).m_type = m_type;
BuildSegment(m_segments[idx] as UniSplineSegment, spt1, spt2, spt3, spt4);
++idx;
}
}
else
{
while (idx < count)
{
spt1 = m_points[Mathf.Clamp(idx, 0, m_points.Count - 1)].m_point;
spt2 = m_points[Mathf.Clamp(idx - 1, 0, m_points.Count - 1)].m_point;
spt3 = m_points[Mathf.Clamp(idx + 2, 0, m_points.Count - 1)].m_point;
spt4 = m_points[Mathf.Clamp(idx + 1, 0, m_points.Count - 1)].m_point;
m_segments[idx] = new UniSplineSegment();
(m_segments[idx] as UniSplineSegment).m_type = m_type;
BuildSegment(m_segments[idx] as UniSplineSegment, spt1, spt2, spt3, spt4);
++idx;
}
}
++m_buildnum;
}
private void BuildSegment(UniSplineSegment ss, Vector3 sp, Vector3 sc, Vector3 ec, Vector3 ep)
{
ss.m_startpos = sp;
ss.m_endpos = ep;
switch (m_type)
{
case UniSplineType.CatmullRom:
ss.m_startctrl = sc;
ss.m_endctrl = ec;
break;
case UniSplineType.Hermite:
{
float tens = (1 - m_tension) * 0.5f,
coef1 = (1 + m_bias) * tens,
coef2 = (1 - m_bias) * tens;
ss.m_startctrl = (sp - sc) * coef1 + (ep - sp) * coef2;
ss.m_endctrl = (ep - sp) * coef1 + (ec - ep) * coef2;
}
break;
case UniSplineType.KochanekBartels:
{
float c1 = (1 - m_tension) * (1 + m_bias) * (1 + m_continuity) * 0.5f,
c2 = (1 - m_tension) * (1 - m_bias) * (1 - m_continuity) * 0.5f,
c3 = (1 - m_tension) * (1 + m_bias) * (1 - m_continuity) * 0.5f,
c4 = (1 - m_tension) * (1 - m_bias) * (1 + m_continuity) * 0.5f;
ss.m_startctrl = c1 * (sp - sc) + c2 * (ep - sp);
ss.m_endctrl = c3 * (ep - sp) + c4 * (ec - ep);
}
break;
}
ss.m_startlen = m_length;
float seglen = GetLength(ss);
m_length += seglen;
ss.m_length = seglen;
ss.m_endlen = m_length;
switch (m_reparam)
{
case SplineReparamType.None:
ss.m_params = null;
ss.m_precomps = null;
break;
case SplineReparamType.Simple:
{
m_precompdiv = 1 / (float)m_stepcount;
float param = 0, length = 0;
Vector3 prev, next;
ss.m_params = new float[m_stepcount + 1];
ss.m_precomps = new float[m_stepcount + 1];
for (int i = 1; i < m_stepcount + 1; ++i)
{
prev = ss.GetPosition(param);
param += m_precompdiv;
next = ss.GetPosition(param);
length += (next - prev).magnitude;
ss.m_precomps[i] = length / seglen;
ss.m_params[i] = param;
}
ss.m_params[0] = 0;
ss.m_params[m_stepcount] = 1;
ss.m_precomps[0] = 0;
ss.m_precomps[m_stepcount] = 1;
m_precompdiv = 1 / (float)m_stepcount;
}
break;
case SplineReparamType.RungeKutta:
float dlen = seglen / (float)m_stepcount, lparam = 0;
ss.m_params = new float[m_stepcount + 1];
ss.m_precomps = new float[m_stepcount + 1];
for (int i = 0; i < m_stepcount + 1; ++i)
{
ss.m_params[i] = GetReparamRungeKutta(ss, lparam);
ss.m_precomps[i] = lparam / seglen;
lparam += dlen;
}
ss.m_params[0] = 0;
ss.m_params[m_stepcount] = 1;
ss.m_precomps[0] = 0;
ss.m_precomps[m_stepcount] = 1;
m_precompdiv = 1 / (float)m_stepcount;
break;
}
}
private float GetLength(UniSplineSegment ss)
{
float len = 0;
Vector3 start, end;
float t = 0, dt = 1 / (float)m_stepcount;
int idx = 0;
start = ss.m_startpos;
while (idx < m_stepcount)
{
t += dt;
end = ss.GetPosition(t);
len += (end - start).magnitude;
start = end;
++idx;
}
return len;
}
public override int GetPointCount() { return m_points.Count; }
public override int GetSegmentCount()
{
if (m_segments != null)
{
return m_segments.Length;
}
return 0;
}
protected override float GetSegmentLength(int segidx)
{
return m_segments[segidx].m_length;
}
protected override float GetSegmentStartLength(int segidx)
{
return m_segments[segidx].m_startlen;
}
protected override float GetSegmentEndLength(int segidx)
{
return m_segments[segidx].m_endlen;
}
protected override int FindSegment(float offset)
{
for (int i = 0; i < m_segments.Length; ++i)
{
if (m_segments[i].m_startlen <= offset && m_segments[i].m_endlen > offset)
{
return i;
}
}
return m_segments.Length - 1;
}
protected override Vector3 GetDrawPosition(int segidx, float segpos)
{
return m_segments[segidx].GetPosition(segpos);
}
protected override Vector3 GetPosition(int segidx, float segpos)
{
BaseSplineSegment ss = m_segments[segidx];
if (m_reparam == SplineReparamType.None)
{
return ss.GetPosition(segpos / ss.m_length);
}
else
{
return ss.GetPosition(GetReparam(ss, segpos / ss.m_length));
}
}
protected override Vector3 GetTangent(int segidx, float segpos)
{
BaseSplineSegment ss = m_segments[segidx];
if (m_reparam == SplineReparamType.None)
{
return ss.GetTangent(segpos / ss.m_length);
}
else
{
return ss.GetTangent(GetReparam(ss, segpos / ss.m_length));
}
}
protected override Vector3 GetNormal(int segidx, float segpos)
{
BaseSplineSegment ss = m_segments[segidx];
if (m_reparam == SplineReparamType.None)
{
return ss.GetNormal(segpos / ss.m_length);
}
else
{
return ss.GetNormal(GetReparam(ss, segpos / ss.m_length));
}
}
internal override void AppendPoint(BaseSplinePoint p)
{
m_points.Add(p);
}
internal override void InsertPoint(int idx, BaseSplinePoint p)
{
if (idx < 0 || idx > m_points.Count)
{
throw (new IndexOutOfRangeException());
}
m_points.Insert(idx, p);
}
internal override void RemovePoint(int idx)
{
if (idx < 0 || idx > m_points.Count)
{
throw (new IndexOutOfRangeException());
}
m_points.RemoveAt(idx);
}
internal override Vector3 GetPointPosition(int idx)
{
if (idx < 0 || idx > m_points.Count)
{
throw (new IndexOutOfRangeException());
}
return m_points[idx].m_point;
}
public override BaseEditHelper GetEditHelper()
{
return new EditHelper(this);
}
}
public class UniSplineComponent : MonoBehaviour
{
[SerializeField]
private UniSpline m_spline = new UniSpline();
private int m_buildnum = -1;
private Vector3[] m_drawcache;
public UniSpline Spline
{
get { return m_spline; }
}
void OnDrawGizmosSelected()
{
DrawGizmos(Color.red);
}
void OnDrawGizmos()
{
DrawGizmos(Color.white);
}
public void DrawGizmos(Color color)
{
int curbuild = m_spline.BuildNum;
if (m_spline.GetSegmentCount() > 0)
{
if (m_buildnum != curbuild)
{
m_drawcache = m_spline.GenerateDrawPoints(16);
m_buildnum = curbuild;
}
if (m_drawcache != null)
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = color;
Vector3 startpos = Vector3.zero, endpos = Vector3.zero;
for (int i = 0; i < m_drawcache.Length; ++i)
{
endpos = m_drawcache[i];
if (i != 0)
{
Gizmos.DrawLine(startpos, endpos);
}
startpos = endpos;
}
}
}
}
}
}