You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			475 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			475 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
| using System.Collections.Generic;
 | |
| using UnityEngine;
 | |
| 
 | |
| namespace FairyGUI
 | |
| {
 | |
|     /// <summary>
 | |
|     /// 
 | |
|     /// </summary>
 | |
|     [System.Serializable]
 | |
|     public struct GPathPoint
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public Vector3 pos;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public Vector3 control1;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public Vector3 control2;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public CurveType curveType;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public bool smooth;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public enum CurveType
 | |
|         {
 | |
|             CRSpline,
 | |
|             Bezier,
 | |
|             CubicBezier,
 | |
|             Straight
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="pos"></param>
 | |
|         public GPathPoint(Vector3 pos)
 | |
|         {
 | |
|             this.pos = pos;
 | |
|             this.control1 = Vector3.zero;
 | |
|             this.control2 = Vector3.zero;
 | |
|             this.curveType = CurveType.CRSpline;
 | |
|             this.smooth = true;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="pos"></param>
 | |
|         /// <param name="control"></param>
 | |
|         public GPathPoint(Vector3 pos, Vector3 control)
 | |
|         {
 | |
|             this.pos = pos;
 | |
|             this.control1 = control;
 | |
|             this.control2 = Vector3.zero;
 | |
|             this.curveType = CurveType.Bezier;
 | |
|             this.smooth = true;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="pos"></param>
 | |
|         /// <param name="control1"></param>
 | |
|         /// <param name="control2"></param>
 | |
|         public GPathPoint(Vector3 pos, Vector3 control1, Vector3 control2)
 | |
|         {
 | |
|             this.pos = pos;
 | |
|             this.control1 = control1;
 | |
|             this.control2 = control2;
 | |
|             this.curveType = CurveType.CubicBezier;
 | |
|             this.smooth = true;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="pos"></param>
 | |
|         /// <param name="curveType"></param>
 | |
|         public GPathPoint(Vector3 pos, CurveType curveType)
 | |
|         {
 | |
|             this.pos = pos;
 | |
|             this.control1 = Vector3.zero;
 | |
|             this.control2 = Vector3.zero;
 | |
|             this.curveType = curveType;
 | |
|             this.smooth = true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// 
 | |
|     /// </summary>
 | |
|     public class GPath
 | |
|     {
 | |
|         protected struct Segment
 | |
|         {
 | |
|             public GPathPoint.CurveType type;
 | |
|             public float length;
 | |
|             public int ptStart;
 | |
|             public int ptCount;
 | |
|         }
 | |
| 
 | |
|         protected List<Segment> _segments;
 | |
|         protected List<Vector3> _points;
 | |
|         protected float _fullLength;
 | |
| 
 | |
|         static List<GPathPoint> helperList = new List<GPathPoint>();
 | |
|         static List<Vector3> splinePoints = new List<Vector3>();
 | |
| 
 | |
|         public GPath()
 | |
|         {
 | |
|             _segments = new List<Segment>();
 | |
|             _points = new List<Vector3>();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public float length
 | |
|         {
 | |
|             get { return _fullLength; }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="pt1"></param>
 | |
|         /// <param name="pt2"></param>
 | |
|         public void Create(GPathPoint pt1, GPathPoint pt2)
 | |
|         {
 | |
|             helperList.Clear();
 | |
|             helperList.Add(pt1);
 | |
|             helperList.Add(pt2);
 | |
|             Create(helperList);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="pt1"></param>
 | |
|         /// <param name="pt2"></param>
 | |
|         /// <param name="pt3"></param>
 | |
|         public void Create(GPathPoint pt1, GPathPoint pt2, GPathPoint pt3)
 | |
|         {
 | |
|             helperList.Clear();
 | |
|             helperList.Add(pt1);
 | |
|             helperList.Add(pt2);
 | |
|             helperList.Add(pt3);
 | |
|             Create(helperList);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="pt1"></param>
 | |
|         /// <param name="pt2"></param>
 | |
|         /// <param name="pt3"></param>
 | |
|         /// <param name="pt4"></param>
 | |
|         public void Create(GPathPoint pt1, GPathPoint pt2, GPathPoint pt3, GPathPoint pt4)
 | |
|         {
 | |
|             helperList.Clear();
 | |
|             helperList.Add(pt1);
 | |
|             helperList.Add(pt2);
 | |
|             helperList.Add(pt3);
 | |
|             helperList.Add(pt4);
 | |
|             Create(helperList);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="points"></param>
 | |
|         public void Create(IEnumerable<GPathPoint> points)
 | |
|         {
 | |
|             _segments.Clear();
 | |
|             _points.Clear();
 | |
|             splinePoints.Clear();
 | |
|             _fullLength = 0;
 | |
| 
 | |
|             var et = points.GetEnumerator();
 | |
|             if (!et.MoveNext())
 | |
|                 return;
 | |
| 
 | |
|             GPathPoint prev = et.Current;
 | |
|             if (prev.curveType == GPathPoint.CurveType.CRSpline)
 | |
|                 splinePoints.Add(prev.pos);
 | |
| 
 | |
|             while (et.MoveNext())
 | |
|             {
 | |
|                 GPathPoint current = et.Current;
 | |
| 
 | |
|                 if (prev.curveType != GPathPoint.CurveType.CRSpline)
 | |
|                 {
 | |
|                     Segment seg = new Segment();
 | |
|                     seg.type = prev.curveType;
 | |
|                     seg.ptStart = _points.Count;
 | |
|                     if (prev.curveType == GPathPoint.CurveType.Straight)
 | |
|                     {
 | |
|                         seg.ptCount = 2;
 | |
|                         _points.Add(prev.pos);
 | |
|                         _points.Add(current.pos);
 | |
|                     }
 | |
|                     else if (prev.curveType == GPathPoint.CurveType.Bezier)
 | |
|                     {
 | |
|                         seg.ptCount = 3;
 | |
|                         _points.Add(prev.pos);
 | |
|                         _points.Add(current.pos);
 | |
|                         _points.Add(prev.control1);
 | |
|                     }
 | |
|                     else if (prev.curveType == GPathPoint.CurveType.CubicBezier)
 | |
|                     {
 | |
|                         seg.ptCount = 4;
 | |
|                         _points.Add(prev.pos);
 | |
|                         _points.Add(current.pos);
 | |
|                         _points.Add(prev.control1);
 | |
|                         _points.Add(prev.control2);
 | |
|                     }
 | |
|                     seg.length = Vector3.Distance(prev.pos, current.pos);
 | |
|                     _fullLength += seg.length;
 | |
|                     _segments.Add(seg);
 | |
|                 }
 | |
| 
 | |
|                 if (current.curveType != GPathPoint.CurveType.CRSpline)
 | |
|                 {
 | |
|                     if (splinePoints.Count > 0)
 | |
|                     {
 | |
|                         splinePoints.Add(current.pos);
 | |
|                         CreateSplineSegment();
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                     splinePoints.Add(current.pos);
 | |
| 
 | |
|                 prev = current;
 | |
|             }
 | |
| 
 | |
|             if (splinePoints.Count > 1)
 | |
|                 CreateSplineSegment();
 | |
|         }
 | |
| 
 | |
|         void CreateSplineSegment()
 | |
|         {
 | |
|             int cnt = splinePoints.Count;
 | |
|             splinePoints.Insert(0, splinePoints[0]);
 | |
|             splinePoints.Add(splinePoints[cnt]);
 | |
|             splinePoints.Add(splinePoints[cnt]);
 | |
|             cnt += 3;
 | |
| 
 | |
|             Segment seg = new Segment();
 | |
|             seg.type = GPathPoint.CurveType.CRSpline;
 | |
|             seg.ptStart = _points.Count;
 | |
|             seg.ptCount = cnt;
 | |
|             _points.AddRange(splinePoints);
 | |
| 
 | |
|             seg.length = 0;
 | |
|             for (int i = 1; i < cnt; i++)
 | |
|                 seg.length += Vector3.Distance(splinePoints[i - 1], splinePoints[i]);
 | |
|             _fullLength += seg.length;
 | |
|             _segments.Add(seg);
 | |
|             splinePoints.Clear();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public void Clear()
 | |
|         {
 | |
|             _segments.Clear();
 | |
|             _points.Clear();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="t"></param>
 | |
|         /// <returns></returns>
 | |
|         public Vector3 GetPointAt(float t)
 | |
|         {
 | |
|             t = Mathf.Clamp01(t);
 | |
|             int cnt = _segments.Count;
 | |
|             if (cnt == 0)
 | |
|                 return Vector3.zero;
 | |
| 
 | |
|             Segment seg;
 | |
|             if (t == 1)
 | |
|             {
 | |
|                 seg = _segments[cnt - 1];
 | |
| 
 | |
|                 if (seg.type == GPathPoint.CurveType.Straight)
 | |
|                     return Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t);
 | |
|                 else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)
 | |
|                     return onBezierCurve(seg.ptStart, seg.ptCount, t);
 | |
|                 else
 | |
|                     return onCRSplineCurve(seg.ptStart, seg.ptCount, t);
 | |
|             }
 | |
| 
 | |
|             float len = t * _fullLength;
 | |
|             Vector3 pt = new Vector3();
 | |
|             for (int i = 0; i < cnt; i++)
 | |
|             {
 | |
|                 seg = _segments[i];
 | |
| 
 | |
|                 len -= seg.length;
 | |
|                 if (len < 0)
 | |
|                 {
 | |
|                     t = 1 + len / seg.length;
 | |
| 
 | |
|                     if (seg.type == GPathPoint.CurveType.Straight)
 | |
|                         pt = Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t);
 | |
|                     else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)
 | |
|                         pt = onBezierCurve(seg.ptStart, seg.ptCount, t);
 | |
|                     else
 | |
|                         pt = onCRSplineCurve(seg.ptStart, seg.ptCount, t);
 | |
| 
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return pt;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public int segmentCount
 | |
|         {
 | |
|             get { return _segments.Count; }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="segmentIndex"></param>
 | |
|         /// <returns></returns>
 | |
|         public float GetSegmentLength(int segmentIndex)
 | |
|         {
 | |
|             return _segments[segmentIndex].length;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="segmentIndex"></param>
 | |
|         /// <param name="t0"></param>
 | |
|         /// <param name="t1"></param>
 | |
|         /// <param name="points"></param>
 | |
|         /// <param name="ts"></param>
 | |
|         public void GetPointsInSegment(int segmentIndex, float t0, float t1, List<Vector3> points, List<float> ts = null, float pointDensity = 0.1f)
 | |
|         {
 | |
|             if (points == null)
 | |
|                 points = new List<Vector3>();
 | |
| 
 | |
|             if (ts != null)
 | |
|                 ts.Add(t0);
 | |
|             Segment seg = _segments[segmentIndex];
 | |
|             if (seg.type == GPathPoint.CurveType.Straight)
 | |
|             {
 | |
|                 points.Add(Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t0));
 | |
|                 points.Add(Vector3.Lerp(_points[seg.ptStart], _points[seg.ptStart + 1], t1));
 | |
|             }
 | |
|             else if (seg.type == GPathPoint.CurveType.Bezier || seg.type == GPathPoint.CurveType.CubicBezier)
 | |
|             {
 | |
|                 points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t0));
 | |
|                 int SmoothAmount = (int)Mathf.Min(seg.length * pointDensity, 50);
 | |
|                 for (int j = 0; j <= SmoothAmount; j++)
 | |
|                 {
 | |
|                     float t = (float)j / SmoothAmount;
 | |
|                     if (t > t0 && t < t1)
 | |
|                     {
 | |
|                         points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t));
 | |
|                         if (ts != null)
 | |
|                             ts.Add(t);
 | |
|                     }
 | |
|                 }
 | |
|                 points.Add(onBezierCurve(seg.ptStart, seg.ptCount, t1));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t0));
 | |
|                 int SmoothAmount = (int)Mathf.Min(seg.length * pointDensity, 50);
 | |
|                 for (int j = 0; j <= SmoothAmount; j++)
 | |
|                 {
 | |
|                     float t = (float)j / SmoothAmount;
 | |
|                     if (t > t0 && t < t1)
 | |
|                     {
 | |
|                         points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t));
 | |
|                         if (ts != null)
 | |
|                             ts.Add(t);
 | |
|                     }
 | |
|                 }
 | |
|                 points.Add(onCRSplineCurve(seg.ptStart, seg.ptCount, t1));
 | |
|             }
 | |
| 
 | |
|             if (ts != null)
 | |
|                 ts.Add(t1);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         /// <param name="points"></param>
 | |
|         public void GetAllPoints(List<Vector3> points, float pointDensity = 0.1f)
 | |
|         {
 | |
|             int cnt = _segments.Count;
 | |
|             for (int i = 0; i < cnt; i++)
 | |
|                 GetPointsInSegment(i, 0, 1, points, null, pointDensity);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Catmull rom spline implementation
 | |
|         /// by Stéphane Drouot, laei - http://games.laei.org
 | |
|         /// 
 | |
|         /// Actual translation of math gebrish to C# credit is due to 
 | |
|         /// Boon Cotter - http://www.booncotter.com/waypoints-catmull-rom-splines/
 | |
|         /// 
 | |
|         /// This takes a list of vector3 (or an array) and gives a function called .onCurve(t)
 | |
|         /// returning a value on a Catmull-Rom spline for 0 <= t <= 1
 | |
|         /// </summary>
 | |
|         Vector3 onCRSplineCurve(int ptStart, int ptCount, float t)
 | |
|         {
 | |
|             int adjustedIndex = Mathf.FloorToInt(t * (ptCount - 4)) + ptStart; //Since the equation works with 4 points, we adjust the starting point depending on t to return a point on the specific segment
 | |
| 
 | |
|             Vector3 result = new Vector3();
 | |
| 
 | |
|             Vector3 p0 = _points[adjustedIndex];
 | |
|             Vector3 p1 = _points[adjustedIndex + 1];
 | |
|             Vector3 p2 = _points[adjustedIndex + 2];
 | |
|             Vector3 p3 = _points[adjustedIndex + 3];
 | |
| 
 | |
|             float adjustedT = (t == 1f) ? 1f : Mathf.Repeat(t * (ptCount - 4), 1f); // Then we adjust t to be that value on that new piece of segment... for t == 1f don't use repeat (that would return 0f);
 | |
| 
 | |
|             float t0 = ((-adjustedT + 2f) * adjustedT - 1f) * adjustedT * 0.5f;
 | |
|             float t1 = (((3f * adjustedT - 5f) * adjustedT) * adjustedT + 2f) * 0.5f;
 | |
|             float t2 = ((-3f * adjustedT + 4f) * adjustedT + 1f) * adjustedT * 0.5f;
 | |
|             float t3 = ((adjustedT - 1f) * adjustedT * adjustedT) * 0.5f;
 | |
| 
 | |
|             result.x = p0.x * t0 + p1.x * t1 + p2.x * t2 + p3.x * t3;
 | |
|             result.y = p0.y * t0 + p1.y * t1 + p2.y * t2 + p3.y * t3;
 | |
|             result.z = p0.z * t0 + p1.z * t1 + p2.z * t2 + p3.z * t3;
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         Vector3 onBezierCurve(int ptStart, int ptCount, float t)
 | |
|         {
 | |
|             float t2 = 1f - t;
 | |
|             Vector3 p0 = _points[ptStart];
 | |
|             Vector3 p1 = _points[ptStart + 1];
 | |
|             Vector3 cp0 = _points[ptStart + 2];
 | |
| 
 | |
|             if (ptCount == 4)
 | |
|             {
 | |
|                 Vector3 cp1 = _points[ptStart + 3];
 | |
|                 return t2 * t2 * t2 * p0 + 3f * t2 * t2 * t * cp0 + 3f * t2 * t * t * cp1 + t * t * t * p1;
 | |
|             }
 | |
|             else
 | |
|                 return t2 * t2 * p0 + 2f * t2 * t * cp0 + t * t * p1;
 | |
|         }
 | |
|     }
 | |
| } |