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.
552 lines
19 KiB
552 lines
19 KiB
using UnityEngine;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
namespace uc.Spline
|
|
{
|
|
[Serializable]
|
|
public class BezierSplinePoint : BaseSplinePoint
|
|
{
|
|
public BezierSplinePoint(Vector3 p, Vector3 c1, Vector3 c2, SplinePointType t)
|
|
: base(p)
|
|
{
|
|
m_prevctrl = c1;
|
|
m_nextctrl = c2;
|
|
m_type = t;
|
|
}
|
|
public BezierSplinePoint(Vector3 p)
|
|
: base(p)
|
|
{
|
|
m_prevctrl = Vector3.zero;
|
|
m_nextctrl = Vector3.zero;
|
|
m_type = SplinePointType.Corner;
|
|
}
|
|
|
|
public Vector3 m_prevctrl, m_nextctrl;
|
|
public SplinePointType m_type;
|
|
}
|
|
|
|
[Serializable]
|
|
public class BezierSpline : BaseSpline
|
|
{
|
|
public class EditHelper : BaseEditHelper
|
|
{
|
|
protected new BezierSpline m_spline;
|
|
internal EditHelper(BezierSpline 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.PointCount - 1) + Vector3.right;
|
|
}
|
|
else if(m_spline.PointCount > 1)
|
|
{
|
|
newPos = m_spline.GetPointPosition(m_spline.PointCount - 1) * 2 - m_spline.GetPointPosition(m_spline.PointCount - 2);
|
|
}
|
|
BezierSplinePoint p = new BezierSplinePoint(newPos);
|
|
m_spline.AppendPoint(p);
|
|
m_selidx = m_spline.PointCount - 1;
|
|
}
|
|
|
|
public override void InsertBefore()
|
|
{
|
|
if (m_spline.PointCount == 1)
|
|
{
|
|
BezierSplinePoint p = new BezierSplinePoint(m_spline.GetPointPosition(m_spline.PointCount - 1) + Vector3.left);
|
|
m_spline.InsertPoint(0, p);
|
|
}
|
|
else
|
|
{
|
|
int previdx = m_selidx;
|
|
m_selidx = (int)Mathf.Repeat(m_selidx - 1, m_spline.PointCount - 1);
|
|
BezierSplinePoint p = new BezierSplinePoint((m_spline.GetPointPosition(m_selidx) + m_spline.GetPointPosition(previdx)) * 0.5f);
|
|
m_spline.InsertPoint(previdx, p);
|
|
m_selidx = previdx;
|
|
}
|
|
}
|
|
|
|
public override void InsertAfter()
|
|
{
|
|
if (m_spline.m_points.Count == 1)
|
|
{
|
|
AppendPoint();
|
|
}
|
|
else
|
|
{
|
|
int previdx = m_selidx;
|
|
m_selidx = (int)Mathf.Repeat(m_selidx + 1, m_spline.PointCount - 1);
|
|
BezierSplinePoint p = new BezierSplinePoint((m_spline.GetPointPosition(m_selidx) + m_spline.GetPointPosition(previdx)) * 0.5f);
|
|
m_spline.InsertPoint(m_selidx, p);
|
|
}
|
|
}
|
|
|
|
public override void Remove()
|
|
{
|
|
m_spline.RemovePoint(m_selidx);
|
|
m_selidx = (int)Mathf.Repeat(m_selidx, m_spline.PointCount - 1);
|
|
}
|
|
|
|
private int m_selcpidx = -1;
|
|
|
|
public override bool Selected
|
|
{
|
|
get { return base.Selected; }
|
|
set { base.Selected = value; m_selcpidx = -1; }
|
|
}
|
|
|
|
public override BaseSplinePoint Point
|
|
{
|
|
get { return m_spline.m_points[m_idx]; }
|
|
set { m_spline.m_points[m_idx] = value as BezierSplinePoint; }
|
|
}
|
|
|
|
public override BaseSplinePoint SelectedPoint
|
|
{
|
|
get { return m_spline.m_points[m_selidx]; }
|
|
set { m_spline.m_points[m_selidx] = value as BezierSplinePoint; }
|
|
}
|
|
|
|
public bool ControlPoint1Selected
|
|
{
|
|
get { return m_selcpidx == 0; }
|
|
set { if (value) { m_selcpidx = 0; } else { m_selcpidx = -1; } }
|
|
}
|
|
|
|
public bool ControlPoint2Selected
|
|
{
|
|
get { return m_selcpidx == 1; }
|
|
set { if (value) { m_selcpidx = 1; } else { m_selcpidx = -1; } }
|
|
}
|
|
|
|
public bool ControlPointsVisible
|
|
{
|
|
get
|
|
{
|
|
if (m_selidx == -1) return false;
|
|
BezierSplinePoint p = m_spline.m_points[m_selidx] as BezierSplinePoint;
|
|
return (p.m_type == SplinePointType.Bezier ||
|
|
p.m_type == SplinePointType.BezierCorner);
|
|
}
|
|
}
|
|
|
|
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 BezierSplineSegment : BaseSplineSegment
|
|
{
|
|
public SplineSegmentType m_type;
|
|
internal override Vector3 GetPosition(float t)
|
|
{
|
|
switch (m_type)
|
|
{
|
|
case SplineSegmentType.Linear:
|
|
return m_startpos + (m_endpos - m_startpos) * t;
|
|
case SplineSegmentType.Curve:
|
|
return uc.Math.Spline.Bezier.Position(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
|
|
default: return Vector3.zero;
|
|
}
|
|
}
|
|
internal override Vector3 GetTangent(float t)
|
|
{
|
|
switch (m_type)
|
|
{
|
|
case SplineSegmentType.Linear:
|
|
return (m_endpos - m_startpos);
|
|
case SplineSegmentType.Curve:
|
|
return uc.Math.Spline.Bezier.Tangent(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
|
|
default: return Vector3.zero;
|
|
}
|
|
}
|
|
internal override Vector3 GetNormal(float t)
|
|
{
|
|
switch (m_type)
|
|
{
|
|
case SplineSegmentType.Linear:
|
|
return Vector3.zero;
|
|
case SplineSegmentType.Curve:
|
|
return uc.Math.Spline.Bezier.Normal(m_startctrl, m_startpos, m_endpos, m_endctrl, t);
|
|
default: return Vector3.zero;
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private List<BezierSplinePoint> m_points = new List<BezierSplinePoint>();
|
|
[SerializeField]
|
|
private BezierSplineSegment[] m_segments = null;
|
|
public override int PointCount { get { return m_points.Count; } }
|
|
|
|
public void ReversePoints()
|
|
{
|
|
m_points.Reverse();
|
|
Vector3 swp;
|
|
for (int i = 0; i < m_points.Count; ++i)
|
|
{
|
|
BezierSplinePoint p = m_points[i] as BezierSplinePoint;
|
|
|
|
swp = p.m_nextctrl;
|
|
p.m_nextctrl = p.m_prevctrl;
|
|
p.m_prevctrl = swp;
|
|
}
|
|
}
|
|
|
|
public override void Build()
|
|
{
|
|
int idx, count;
|
|
BezierSplinePoint pp, sp, ep, np;
|
|
//
|
|
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 BezierSplineSegment[count];
|
|
m_length = 0;
|
|
idx = 0;
|
|
if (m_wrapmode == SplineWrapMode.Loop)
|
|
{
|
|
while (idx < count)
|
|
{
|
|
pp = m_points[(int)Mathf.Repeat(idx - 1, m_points.Count)] as BezierSplinePoint;
|
|
sp = m_points[(int)Mathf.Repeat(idx, m_points.Count)] as BezierSplinePoint;
|
|
ep = m_points[(int)Mathf.Repeat(idx + 1, m_points.Count)] as BezierSplinePoint;
|
|
np = m_points[(int)Mathf.Repeat(idx + 2, m_points.Count)] as BezierSplinePoint;
|
|
|
|
m_segments[idx] = new BezierSplineSegment();
|
|
BuildSegment(m_segments[idx] as BezierSplineSegment, pp, sp, ep, np);
|
|
++idx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (idx < count)
|
|
{
|
|
pp = m_points[Mathf.Clamp(idx - 1, 0, m_points.Count - 1)] as BezierSplinePoint;
|
|
sp = m_points[Mathf.Clamp(idx, 0, m_points.Count - 1)] as BezierSplinePoint;
|
|
ep = m_points[Mathf.Clamp(idx + 1, 0, m_points.Count - 1)] as BezierSplinePoint;
|
|
np = m_points[Mathf.Clamp(idx + 2, 0, m_points.Count - 1)] as BezierSplinePoint;
|
|
|
|
m_segments[idx] = new BezierSplineSegment();
|
|
BuildSegment(m_segments[idx] as BezierSplineSegment, pp, sp, ep, np);
|
|
++idx;
|
|
}
|
|
}
|
|
|
|
++m_buildnum;
|
|
}
|
|
|
|
private void BuildSegment(BezierSplineSegment ss, BezierSplinePoint pp, BezierSplinePoint sp, BezierSplinePoint ep, BezierSplinePoint np)
|
|
{
|
|
PreparePoint(pp, sp, ep);
|
|
PreparePoint(sp, ep, np);
|
|
|
|
ss.m_startpos = sp.m_point;
|
|
ss.m_endpos = ep.m_point;
|
|
ss.m_startctrl = ss.m_startpos + sp.m_nextctrl;
|
|
ss.m_endctrl = ss.m_endpos + ep.m_prevctrl;
|
|
|
|
if (sp.m_type == SplinePointType.Corner && ep.m_type == SplinePointType.Corner)
|
|
{
|
|
ss.m_type = SplineSegmentType.Linear;
|
|
}
|
|
else
|
|
{
|
|
ss.m_type = SplineSegmentType.Curve;
|
|
}
|
|
|
|
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 void PreparePoint(BezierSplinePoint pp, BezierSplinePoint pt, BezierSplinePoint np)
|
|
{
|
|
switch (pt.m_type)
|
|
{
|
|
case SplinePointType.Bezier:
|
|
pt.m_nextctrl = -pt.m_prevctrl;
|
|
break;
|
|
|
|
case SplinePointType.Smooth:
|
|
pt.m_prevctrl = -0.25f * (np.m_point - pp.m_point);
|
|
pt.m_nextctrl = -0.25f * (pp.m_point - np.m_point);
|
|
break;
|
|
|
|
case SplinePointType.Corner:
|
|
pt.m_prevctrl = Vector3.zero;
|
|
pt.m_nextctrl = Vector3.zero;
|
|
break;
|
|
|
|
case SplinePointType.BezierCorner:
|
|
break;
|
|
}
|
|
}
|
|
|
|
private float GetLength(BezierSplineSegment ss)
|
|
{
|
|
switch (ss.m_type)
|
|
{
|
|
case SplineSegmentType.Linear:
|
|
return (ss.m_endpos - ss.m_startpos).magnitude;
|
|
|
|
case SplineSegmentType.Curve:
|
|
{
|
|
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;
|
|
}
|
|
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
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 as BezierSplinePoint);
|
|
}
|
|
internal override void InsertPoint(int idx, BaseSplinePoint p)
|
|
{
|
|
if (idx < 0 || idx > m_points.Count)
|
|
{
|
|
throw (new IndexOutOfRangeException());
|
|
}
|
|
m_points.Insert(idx, p as BezierSplinePoint);
|
|
}
|
|
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 BezierSplineComponent : MonoBehaviour
|
|
{
|
|
[SerializeField]
|
|
private BezierSpline m_spline = new BezierSpline();
|
|
|
|
private int m_buildnum = -1;
|
|
private Vector3[] m_drawcache;
|
|
|
|
public BezierSpline Spline
|
|
{
|
|
get { return m_spline; }
|
|
}
|
|
|
|
void OnDrawGizmosSelected()
|
|
{
|
|
DrawGizmos(Color.red);
|
|
}
|
|
|
|
void OnDrawGizmos()
|
|
{
|
|
DrawGizmos(Color.white);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |