CatmullRomSpline.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class CatmullRomSpline
  5. {
  6. public int NbSegments
  7. {
  8. get
  9. {
  10. return Math.Max(0, this.knots.Count - 3);
  11. }
  12. }
  13. public Vector3 FindPositionFromDistance(float distance)
  14. {
  15. Vector3 result = Vector3.zero;
  16. CatmullRomSpline.Marker marker = new CatmullRomSpline.Marker();
  17. bool flag = this.PlaceMarker(marker, distance, null);
  18. if (flag)
  19. {
  20. result = this.GetPosition(marker);
  21. }
  22. return result;
  23. }
  24. public Vector3 FindTangentFromDistance(float distance)
  25. {
  26. Vector3 result = Vector3.zero;
  27. CatmullRomSpline.Marker marker = new CatmullRomSpline.Marker();
  28. bool flag = this.PlaceMarker(marker, distance, null);
  29. if (flag)
  30. {
  31. result = this.GetTangent(marker);
  32. }
  33. return result;
  34. }
  35. public static Vector3 ComputeBinormal(Vector3 tangent, Vector3 normal)
  36. {
  37. return Vector3.Cross(tangent, normal).normalized;
  38. }
  39. public float Length()
  40. {
  41. if (this.NbSegments == 0)
  42. {
  43. return 0f;
  44. }
  45. return Math.Max(0f, this.GetSegmentDistanceFromStart(this.NbSegments - 1));
  46. }
  47. public void Clear()
  48. {
  49. this.knots.Clear();
  50. }
  51. public void MoveMarker(CatmullRomSpline.Marker marker, float distance)
  52. {
  53. this.PlaceMarker(marker, distance, marker);
  54. }
  55. public void SetLastPos(Vector3 lastPos)
  56. {
  57. this.lastPos = lastPos;
  58. }
  59. public Vector3 GetPosition(CatmullRomSpline.Marker marker)
  60. {
  61. Vector3 vector = Vector3.zero;
  62. if (this.NbSegments == 0)
  63. {
  64. return vector;
  65. }
  66. CatmullRomSpline.SubKnot[] segmentSubKnots = this.GetSegmentSubKnots(marker.segmentIndex);
  67. vector = Vector3.Lerp(segmentSubKnots[marker.subKnotAIndex].position, segmentSubKnots[marker.subKnotBIndex].position, marker.lerpRatio);
  68. if (this.targetTrans != null)
  69. {
  70. vector += this.targetTrans.position - this.lastPos;
  71. }
  72. return vector;
  73. }
  74. public Vector3 GetTangent(CatmullRomSpline.Marker marker)
  75. {
  76. Vector3 zero = Vector3.zero;
  77. if (this.NbSegments == 0)
  78. {
  79. return zero;
  80. }
  81. CatmullRomSpline.SubKnot[] segmentSubKnots = this.GetSegmentSubKnots(marker.segmentIndex);
  82. return Vector3.Lerp(segmentSubKnots[marker.subKnotAIndex].tangent, segmentSubKnots[marker.subKnotBIndex].tangent, marker.lerpRatio);
  83. }
  84. private float Epsilon
  85. {
  86. get
  87. {
  88. return 0.1f;
  89. }
  90. }
  91. private CatmullRomSpline.SubKnot[] GetSegmentSubKnots(int i)
  92. {
  93. return this.knots[2 + i].subKnots;
  94. }
  95. public float GetSegmentDistanceFromStart(int i)
  96. {
  97. return this.knots[2 + i].distanceFromStart;
  98. }
  99. private bool IsSegmentValid(int i)
  100. {
  101. return this.knots[i].distanceFromStart != -1f && this.knots[i + 1].distanceFromStart != -1f && this.knots[i + 2].distanceFromStart != -1f && this.knots[i + 3].distanceFromStart != -1f;
  102. }
  103. private bool OutOfBoundSegmentIndex(int i)
  104. {
  105. return i < 0 || i >= this.NbSegments;
  106. }
  107. public void Parametrize()
  108. {
  109. this.Parametrize(0, this.NbSegments - 1);
  110. }
  111. public void Launch(Transform targetTrans)
  112. {
  113. this.targetTrans = targetTrans;
  114. this.lastPos = targetTrans.position;
  115. }
  116. public void Parametrize(int fromSegmentIndex, int toSegmentIndex)
  117. {
  118. if (this.knots.Count < 4)
  119. {
  120. return;
  121. }
  122. int num = Math.Min(toSegmentIndex + 1, this.NbSegments);
  123. fromSegmentIndex = Math.Max(0, fromSegmentIndex);
  124. float num2 = 0f;
  125. if (fromSegmentIndex > 0)
  126. {
  127. num2 = this.GetSegmentDistanceFromStart(fromSegmentIndex - 1);
  128. }
  129. for (int i = fromSegmentIndex; i < num; i++)
  130. {
  131. CatmullRomSpline.SubKnot[] segmentSubKnots = this.GetSegmentSubKnots(i);
  132. for (int j = 0; j < segmentSubKnots.Length; j++)
  133. {
  134. CatmullRomSpline.SubKnot subKnot = default(CatmullRomSpline.SubKnot);
  135. num2 = (subKnot.distanceFromStart = num2 + this.ComputeLengthOfSegment(i, (float)(j - 1) * this.Epsilon, (float)j * this.Epsilon));
  136. subKnot.position = this.GetPositionOnSegment(i, (float)j * this.Epsilon);
  137. subKnot.tangent = this.GetTangentOnSegment(i, (float)j * this.Epsilon);
  138. segmentSubKnots[j] = subKnot;
  139. }
  140. this.knots[2 + i].distanceFromStart = num2;
  141. }
  142. }
  143. public bool PlaceMarker(CatmullRomSpline.Marker result, float distance, CatmullRomSpline.Marker from = null)
  144. {
  145. int nbSegments = this.NbSegments;
  146. if (nbSegments == 0)
  147. {
  148. return false;
  149. }
  150. if (distance <= 0f)
  151. {
  152. result.segmentIndex = 0;
  153. result.subKnotAIndex = 0;
  154. result.subKnotBIndex = 1;
  155. result.lerpRatio = 0f;
  156. return true;
  157. }
  158. if (distance >= this.Length())
  159. {
  160. CatmullRomSpline.SubKnot[] segmentSubKnots = this.GetSegmentSubKnots(nbSegments - 1);
  161. result.segmentIndex = nbSegments - 1;
  162. result.subKnotAIndex = segmentSubKnots.Length - 2;
  163. result.subKnotBIndex = segmentSubKnots.Length - 1;
  164. result.lerpRatio = 1f;
  165. return true;
  166. }
  167. int num = 0;
  168. int num2 = 1;
  169. if (from != null)
  170. {
  171. num = from.segmentIndex;
  172. }
  173. for (int i = num; i < nbSegments; i++)
  174. {
  175. if (distance <= this.GetSegmentDistanceFromStart(i))
  176. {
  177. CatmullRomSpline.SubKnot[] segmentSubKnots = this.GetSegmentSubKnots(i);
  178. for (int j = num2; j < segmentSubKnots.Length; j++)
  179. {
  180. CatmullRomSpline.SubKnot subKnot = segmentSubKnots[j];
  181. if (distance <= subKnot.distanceFromStart)
  182. {
  183. result.segmentIndex = i;
  184. result.subKnotAIndex = j - 1;
  185. result.subKnotBIndex = j;
  186. result.lerpRatio = 1f - (subKnot.distanceFromStart - distance) / (subKnot.distanceFromStart - segmentSubKnots[j - 1].distanceFromStart);
  187. break;
  188. }
  189. }
  190. break;
  191. }
  192. }
  193. return true;
  194. }
  195. private float ComputeLength()
  196. {
  197. if (this.knots.Count < 4)
  198. {
  199. return 0f;
  200. }
  201. float num = 0f;
  202. int nbSegments = this.NbSegments;
  203. for (int i = 0; i < nbSegments; i++)
  204. {
  205. num += this.ComputeLengthOfSegment(i, 0f, 1f);
  206. }
  207. return num;
  208. }
  209. private float ComputeLengthOfSegment(int segmentIndex, float from, float to)
  210. {
  211. float num = 0f;
  212. from = Mathf.Clamp01(from);
  213. to = Mathf.Clamp01(to);
  214. Vector3 b = this.GetPositionOnSegment(segmentIndex, from);
  215. for (float num2 = from + this.Epsilon; num2 < to + this.Epsilon / 2f; num2 += this.Epsilon)
  216. {
  217. Vector3 positionOnSegment = this.GetPositionOnSegment(segmentIndex, num2);
  218. num += Vector3.Distance(positionOnSegment, b);
  219. b = positionOnSegment;
  220. }
  221. return num;
  222. }
  223. public void DebugDrawEquallySpacedDots()
  224. {
  225. Gizmos.color = Color.red;
  226. int num = 10 * this.NbSegments;
  227. float num2 = this.Length();
  228. CatmullRomSpline.Marker marker = new CatmullRomSpline.Marker();
  229. this.PlaceMarker(marker, 0f, null);
  230. for (int i = 0; i <= num; i++)
  231. {
  232. this.MoveMarker(marker, (float)i * (num2 / (float)num));
  233. Vector3 position = this.GetPosition(marker);
  234. Gizmos.DrawWireSphere(position, 0.025f);
  235. }
  236. }
  237. public void DebugDrawSubKnots()
  238. {
  239. Gizmos.color = Color.yellow;
  240. int nbSegments = this.NbSegments;
  241. for (int i = 0; i < nbSegments; i++)
  242. {
  243. CatmullRomSpline.SubKnot[] segmentSubKnots = this.GetSegmentSubKnots(i);
  244. for (int j = 0; j < segmentSubKnots.Length; j++)
  245. {
  246. Gizmos.DrawWireSphere(segmentSubKnots[j].position, 0.025f);
  247. }
  248. }
  249. }
  250. public void ClearTail(int num)
  251. {
  252. if (this.knots.Count >= num)
  253. {
  254. this.knots.RemoveRange(0, num);
  255. }
  256. else
  257. {
  258. this.knots.Clear();
  259. }
  260. }
  261. public void DebugDrawSpline()
  262. {
  263. if (this.knots.Count >= 4)
  264. {
  265. Gizmos.color = Color.green;
  266. Gizmos.DrawWireSphere(this.knots[0].position, 0.2f);
  267. Gizmos.color = Color.red;
  268. Gizmos.DrawWireSphere(this.knots[this.knots.Count - 1].position, 0.2f);
  269. Gizmos.color = Color.blue;
  270. Gizmos.DrawWireSphere(this.knots[this.knots.Count - 2].position, 0.2f);
  271. int nbSegments = this.NbSegments;
  272. for (int i = 0; i < nbSegments; i++)
  273. {
  274. Vector3 vector = this.GetPositionOnSegment(i, 0f);
  275. Gizmos.DrawWireSphere(vector, 0.2f);
  276. for (float num = this.Epsilon; num < 1f + this.Epsilon / 2f; num += this.Epsilon)
  277. {
  278. Vector3 positionOnSegment = this.GetPositionOnSegment(i, num);
  279. UnityEngine.Debug.DrawLine(vector, positionOnSegment, Color.white);
  280. vector = positionOnSegment;
  281. }
  282. }
  283. }
  284. }
  285. private Vector3 GetPositionOnSegment(int segmentIndex, float t)
  286. {
  287. return CatmullRomSpline.FindSplinePoint(this.knots[segmentIndex].position, this.knots[segmentIndex + 1].position, this.knots[segmentIndex + 2].position, this.knots[segmentIndex + 3].position, t);
  288. }
  289. private Vector3 GetTangentOnSegment(int segmentIndex, float t)
  290. {
  291. return CatmullRomSpline.FindSplineTangent(this.knots[segmentIndex].position, this.knots[segmentIndex + 1].position, this.knots[segmentIndex + 2].position, this.knots[segmentIndex + 3].position, t).normalized;
  292. }
  293. private static Vector3 FindSplinePoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
  294. {
  295. Vector3 result = default(Vector3);
  296. float num = t * t;
  297. float num2 = num * t;
  298. result.x = 0.5f * (2f * p1.x + (-p0.x + p2.x) * t + (2f * p0.x - 5f * p1.x + 4f * p2.x - p3.x) * num + (-p0.x + 3f * p1.x - 3f * p2.x + p3.x) * num2);
  299. result.y = 0.5f * (2f * p1.y + (-p0.y + p2.y) * t + (2f * p0.y - 5f * p1.y + 4f * p2.y - p3.y) * num + (-p0.y + 3f * p1.y - 3f * p2.y + p3.y) * num2);
  300. result.z = 0.5f * (2f * p1.z + (-p0.z + p2.z) * t + (2f * p0.z - 5f * p1.z + 4f * p2.z - p3.z) * num + (-p0.z + 3f * p1.z - 3f * p2.z + p3.z) * num2);
  301. return result;
  302. }
  303. private static Vector3 FindSplineTangent(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
  304. {
  305. Vector3 result = default(Vector3);
  306. float num = t * t;
  307. result.x = 0.5f * (-p0.x + p2.x) + (2f * p0.x - 5f * p1.x + 4f * p2.x - p3.x) * t + (-p0.x + 3f * p1.x - 3f * p2.x + p3.x) * num * 1.5f;
  308. result.y = 0.5f * (-p0.y + p2.y) + (2f * p0.y - 5f * p1.y + 4f * p2.y - p3.y) * t + (-p0.y + 3f * p1.y - 3f * p2.y + p3.y) * num * 1.5f;
  309. result.z = 0.5f * (-p0.z + p2.z) + (2f * p0.z - 5f * p1.z + 4f * p2.z - p3.z) * t + (-p0.z + 3f * p1.z - 3f * p2.z + p3.z) * num * 1.5f;
  310. return result;
  311. }
  312. public List<Knot> knots = new List<Knot>();
  313. public const int NbSubSegmentPerSegment = 10;
  314. private Transform targetTrans;
  315. private Vector3 startPos;
  316. private Vector3 lastPos;
  317. private const int MinimumKnotNb = 4;
  318. private const int FirstSegmentKnotIndex = 2;
  319. public struct SubKnot
  320. {
  321. public float distanceFromStart;
  322. public Vector3 position;
  323. public Vector3 tangent;
  324. }
  325. public class Marker
  326. {
  327. public int segmentIndex;
  328. public int subKnotAIndex;
  329. public int subKnotBIndex;
  330. public float lerpRatio;
  331. }
  332. }