PlaygroundSpline.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. using UnityEngine;
  2. using System;
  3. using System.Collections.Generic;
  4. /// <summary>
  5. /// Collection of methods for working with splines.
  6. /// This is based on the great learning tutorial Curves and Splines by Jasper Flick.
  7. ///
  8. /// References:
  9. /// http://catlikecoding.com/unity/tutorials/curves-and-splines/
  10. /// http://answers.unity3d.com/questions/374333/positioning-an-object-on-a-spline-relative-to-play.html
  11. /// </summary>
  12. namespace PlaygroundSplines {
  13. /// <summary>
  14. /// Holds information about a spline and contains functions for working with the nodes and bezier handles.
  15. /// </summary>
  16. [ExecuteInEditMode()]
  17. public class PlaygroundSpline : MonoBehaviour {
  18. /// <summary>
  19. /// The list of nodes and bezier handles making the spline.
  20. /// </summary>
  21. [SerializeField]
  22. private List<Vector3> points = new List<Vector3>();
  23. /// <summary>
  24. /// The modes of the bezier handles.
  25. /// </summary>
  26. [SerializeField]
  27. private List<BezierControlPointMode> modes = new List<BezierControlPointMode>();
  28. /// <summary>
  29. /// Determines if the spline is looping.
  30. /// </summary>
  31. [SerializeField]
  32. private bool loop;
  33. /// <summary>
  34. /// The list of transform nodes to set positions live of an existing node.
  35. /// </summary>
  36. [HideInInspector] public List<TransformNode> transformNodes = new List<TransformNode>();
  37. /// <summary>
  38. /// Determines if the spline time should be reversed. If you'd like to physically reverse the arrays making the spline then call ReverseAllNodes().
  39. /// </summary>
  40. [HideInInspector] public bool reverse;
  41. /// <summary>
  42. /// The time offset of the spline.
  43. /// </summary>
  44. [HideInInspector] public float timeOffset;
  45. /// <summary>
  46. /// The position offset of the spline in relation to its transform.
  47. /// </summary>
  48. [HideInInspector] public Vector3 positionOffset;
  49. [HideInInspector] public Transform splineTransform;
  50. [HideInInspector] public Matrix4x4 splineTransformMx;
  51. [HideInInspector] public List<Transform> usedBy = new List<Transform>();
  52. [HideInInspector] public float fixedVelocityOnNewNode = .5f;
  53. [HideInInspector] public bool moveTransformsAsBeziers = false;
  54. [HideInInspector] public bool exportWithNodeStructure = false;
  55. // Gizmos
  56. public static bool drawSplinePreviews = true;
  57. [HideInInspector] public bool drawGizmo = true;
  58. [HideInInspector] public float bezierWidth = 2f;
  59. #if UNITY_EDITOR
  60. void OnDrawGizmos () {
  61. if (drawSplinePreviews && drawGizmo) {
  62. Color innerBezier = new Color(1f,1f,1f,1f);
  63. Color outerBezier = new Color(.5f,.5f,0,.2f);
  64. Vector3 p0 = ShowPoint(0);
  65. for (int i = 1; i < ControlPointCount; i += 3) {
  66. Vector3 p1 = ShowPoint(i);
  67. Vector3 p2 = ShowPoint(i + 1);
  68. Vector3 p3 = ShowPoint(i + 2);
  69. UnityEditor.Handles.DrawBezier(p0, p3, p1, p2, innerBezier, null, bezierWidth);
  70. UnityEditor.Handles.DrawBezier(p0, p3, p1, p2, outerBezier, null, bezierWidth*10f);
  71. p0 = p3;
  72. }
  73. }
  74. }
  75. Vector3 ShowPoint (int index) {
  76. return transformNodes[index].IsAvailable()? GetPoint(index)+positionOffset : splineTransform.TransformPoint(GetInversePoint(index)+positionOffset);
  77. }
  78. #endif
  79. Vector3 previousPosition;
  80. Quaternion previousRotation;
  81. Vector3 previousScale;
  82. bool isReady;
  83. public bool IsReady () {
  84. return isReady;
  85. }
  86. /// <summary>
  87. /// Adds a user to the spline. This helps keeping track of which objects are using the spline.
  88. /// </summary>
  89. /// <returns><c>true</c>, if user was added, <c>false</c> otherwise.</returns>
  90. /// <param name="">.</param>
  91. public bool AddUser (Transform thisTransform) {
  92. if (!usedBy.Contains(thisTransform)) {
  93. usedBy.Add (thisTransform);
  94. return true;
  95. }
  96. return false;
  97. }
  98. /// <summary>
  99. /// Removes a user from the spline. This helps keeping track of which objects are using the spline.
  100. /// </summary>
  101. /// <returns><c>true</c>, if user was removed, <c>false</c> otherwise.</returns>
  102. /// <param name="">.</param>
  103. public bool RemoveUser (Transform thisTransform) {
  104. if (usedBy.Contains(thisTransform)) {
  105. usedBy.Remove (thisTransform);
  106. return true;
  107. }
  108. return false;
  109. }
  110. /// <summary>
  111. /// Determines whether this spline has the user of passed in transform.
  112. /// </summary>
  113. /// <returns><c>true</c> if this spline has the user of the passed in transform; otherwise, <c>false</c>.</returns>
  114. /// <param name="thisTransform">This transform.</param>
  115. public bool HasUser (Transform thisTransform) {
  116. return usedBy.Contains (thisTransform);
  117. }
  118. /// <summary>
  119. /// Gets or sets a value indicating whether this <see cref="PlaygroundSplines.PlaygroundSpline"/> is set to loop.
  120. /// </summary>
  121. /// <value><c>true</c> if set to loop; otherwise, <c>false</c>.</value>
  122. public bool Loop {
  123. get {
  124. return loop;
  125. }
  126. set {
  127. loop = value;
  128. if (value == true && NodeCount>1) {
  129. modes[modes.Count - 1] = modes[0];
  130. SetControlPoint(0, points[0]);
  131. }
  132. }
  133. }
  134. /// <summary>
  135. /// Gets the control point count.
  136. /// </summary>
  137. /// <value>The control point count.</value>
  138. public int ControlPointCount {
  139. get {
  140. return points.Count;
  141. }
  142. }
  143. /// <summary>
  144. /// Gets the control point.
  145. /// </summary>
  146. /// <returns>The control point.</returns>
  147. /// <param name="index">Index.</param>
  148. public Vector3 GetControlPoint (int index) {
  149. return GetPoint(index);
  150. }
  151. /// <summary>
  152. /// Sets the control point and withdraws the offset.
  153. /// </summary>
  154. /// <param name="index">Index.</param>
  155. /// <param name="point">Point.</param>
  156. /// <param name="offset">Offset.</param>
  157. public void SetControlPoint (int index, Vector3 point, Vector3 offset) {
  158. SetControlPoint(index, point-offset);
  159. }
  160. /// <summary>
  161. /// Sets the control point.
  162. /// </summary>
  163. /// <param name="index">Index.</param>
  164. /// <param name="point">Position.</param>
  165. public void SetControlPoint (int index, Vector3 point) {
  166. if (index<0) index = 0;
  167. if (index % 3 == 0) {
  168. Vector3 delta = (point - GetPoint(index));
  169. Vector3 v;
  170. if (loop) {
  171. if (index == 0) {
  172. //if (!PointHasTransform(1))
  173. {v = GetPoint(1); SetPoint(1, v+delta);}
  174. //if (!PointHasTransform(points.Count-2))
  175. {v = GetPoint(points.Count-2); SetPoint(points.Count-2, v+delta);}
  176. if (moveTransformsAsBeziers || !PointHasTransform(points.Count-1))
  177. {SetPoint(points.Count-1, point);}
  178. } else
  179. if (index == points.Count - 1) {
  180. //if (!PointHasTransform(0))
  181. {SetPoint(0, point);}
  182. //if (!PointHasTransform(1))
  183. {v = GetPoint(1); SetPoint(1, v+delta);}
  184. //if (!PointHasTransform(index-1))
  185. {v = GetPoint(index-1); SetPoint(index-1, v+delta);}
  186. } else {
  187. //if (!PointHasTransform(index-1))
  188. {v = GetPoint(index-1); SetPoint(index-1, v+delta);}
  189. //if (!PointHasTransform(index+1))
  190. {v = GetPoint(index+1); SetPoint(index+1, v+delta);}
  191. }
  192. } else {
  193. if (index > 0) {
  194. if (moveTransformsAsBeziers || !PointHasTransform(index-1))
  195. {v = GetPoint(index-1); SetPoint(index-1, v+delta);}
  196. }
  197. if (index + 1 < points.Count) {
  198. if (moveTransformsAsBeziers || !PointHasTransform(index+1))
  199. {v = GetPoint(index+1); SetPoint(index+1, v+delta);}
  200. }
  201. }
  202. }
  203. SetPoint(index, point);
  204. EnforceMode(index);
  205. }
  206. /// <summary>
  207. /// Sets all points from an array. Please ensure the same length of your passed in vectors as PlaygroundSpline.ControlPointCount.
  208. /// </summary>
  209. /// <param name="vectors">Vectors.</param>
  210. public void SetPoints (Vector3[] vectors) {
  211. if (vectors.Length!=points.Count) {
  212. Debug.Log ("Please ensure the same length of your passed in vectors ("+vectors.Length+") as the current points ("+points.Count+"). Use PlaygroundSpline.ControlPointCount to get the current count.");
  213. return;
  214. }
  215. for (int i = 0; i<points.Count; i++) {
  216. points[i] = vectors[i];
  217. }
  218. }
  219. public bool PointHasTransform (int index) {
  220. return transformNodes[index].IsAvailable();
  221. }
  222. /// <summary>
  223. /// Moves the entire spline separate from its transform component. Use this if you'd like to offset the spline from its transform separately from the positionOffset.
  224. /// </summary>
  225. /// <param name="translation">The amount to move the spline in Units.</param>
  226. public void TranslateSpline (Vector3 translation) {
  227. for (int i = 0; i<points.Count; i++) {
  228. points[i] += translation;
  229. }
  230. }
  231. public Vector3 GetTransformPosition () {
  232. return previousPosition;
  233. }
  234. public Quaternion GetTransformRotation () {
  235. return previousRotation;
  236. }
  237. public Vector3 GetTransformScale () {
  238. return previousScale;
  239. }
  240. public BezierControlPointMode GetControlPointMode (int index) {
  241. return modes[(index + 1) / 3];
  242. }
  243. public void SetControlPointMode (int index, BezierControlPointMode mode) {
  244. int modeIndex = (index + 1) / 3;
  245. modes[modeIndex] = mode;
  246. if (loop) {
  247. if (modeIndex == 0) {
  248. modes[modes.Count - 1] = mode;
  249. }
  250. else if (modeIndex == modes.Count - 1) {
  251. modes[0] = mode;
  252. }
  253. }
  254. EnforceMode(index);
  255. }
  256. private void EnforceMode (int index) {
  257. int modeIndex = (index+1) / 3;
  258. BezierControlPointMode mode = modes[modeIndex];
  259. if (mode == BezierControlPointMode.Free || !loop && (modeIndex == 0 || modeIndex == modes.Count - 1)) {
  260. return;
  261. }
  262. int middleIndex = modeIndex * 3;
  263. int fixedIndex, enforcedIndex;
  264. if (index <= middleIndex) {
  265. fixedIndex = middleIndex - 1;
  266. if (fixedIndex < 0) {
  267. fixedIndex = points.Count - 2;
  268. }
  269. enforcedIndex = middleIndex + 1;
  270. if (enforcedIndex >= points.Count) {
  271. enforcedIndex = 1;
  272. }
  273. }
  274. else {
  275. fixedIndex = middleIndex + 1;
  276. if (fixedIndex >= points.Count) {
  277. fixedIndex = 1;
  278. }
  279. enforcedIndex = middleIndex - 1;
  280. if (enforcedIndex < 0) {
  281. enforcedIndex = points.Count - 2;
  282. }
  283. }
  284. Vector3 middle = GetPoint(middleIndex);
  285. Vector3 enforcedTangent = middle - GetPoint(fixedIndex);
  286. if (mode == BezierControlPointMode.Aligned) {
  287. enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middle, GetPoint(enforcedIndex));
  288. }
  289. if (moveTransformsAsBeziers || !PointHasTransform(enforcedIndex))
  290. SetPoint(enforcedIndex, middle + enforcedTangent);
  291. }
  292. public int NodeCount {
  293. get {
  294. return (points.Count - 1) / 3;
  295. }
  296. }
  297. /// <summary>
  298. /// Get position from time.
  299. /// </summary>
  300. /// <returns>The point in world space.</returns>
  301. /// <param name="t">Time.</param>
  302. public Vector3 GetPoint (float t) {
  303. int i;
  304. if (reverse) {
  305. t = 1f-t;
  306. t = (t-timeOffset)%1f;
  307. if (t<0)
  308. t = 1f+t;
  309. } else t = (t+timeOffset)%1f;
  310. if (t >= 1f) {
  311. //t = 1f;
  312. i = points.Count - 4;
  313. }
  314. else {
  315. t = Mathf.Clamp01(t) * NodeCount;
  316. i = (int)t;
  317. t -= i;
  318. i *= 3;
  319. }
  320. return splineTransformMx.MultiplyPoint3x4(Bezier.GetPoint(GetInversePoint(i), GetInversePoint(i + 1), GetInversePoint(i + 2), GetInversePoint(i + 3), t)+positionOffset);
  321. }
  322. public Vector3 GetVelocity (float t) {
  323. int i;
  324. if (reverse)
  325. t = 1f-t;
  326. t = (t+timeOffset)%1f;
  327. if (t >= 1f) {
  328. t = 1f;
  329. i = points.Count - 4;
  330. }
  331. else {
  332. t = Mathf.Clamp01(t) * NodeCount;
  333. i = (int)t;
  334. t -= i;
  335. i *= 3;
  336. }
  337. return splineTransformMx.MultiplyPoint3x4(Bezier.GetFirstDerivative(GetInversePoint(i), GetInversePoint(i + 1), GetInversePoint(i + 2), GetInversePoint(i + 3), t)+positionOffset) - previousPosition;
  338. }
  339. /// <summary>
  340. /// Get position from node index in the spline. If the node consists of an available transform its position will be returned, otherwise the user-specified Vector3 position.
  341. /// </summary>
  342. /// <returns>The point in world space.</returns>
  343. /// <param name="index">Index.</param>
  344. public Vector3 GetPoint (int index) {
  345. if (transformNodes[index].IsAvailable())
  346. return transformNodes[index].GetPosition();
  347. else return points[index];
  348. }
  349. public Vector3 GetInversePoint (int index) {
  350. if (transformNodes[index].IsAvailable())
  351. return transformNodes[index].GetInvsersePosition();
  352. else return points[index];
  353. }
  354. public Vector3 GetPointWorldSpace (int index) {
  355. if (transformNodes[index].IsAvailable())
  356. return transformNodes[index].GetPosition();
  357. else return splineTransformMx.MultiplyPoint3x4(points[index]+positionOffset);
  358. }
  359. /// <summary>
  360. /// Sets a point to specified position.
  361. /// </summary>
  362. /// <param name="index">Index.</param>
  363. /// <param name="position">Position.</param>
  364. void SetPoint (int index, Vector3 position) {
  365. if (transformNodes[index].IsAvailable())
  366. transformNodes[index].SetPosition(position);
  367. else points[index] = position;
  368. }
  369. /// <summary>
  370. /// Translates a point.
  371. /// </summary>
  372. /// <param name="index">Index.</param>
  373. /// <param name="translation">Translation.</param>
  374. void TranslatePoint (int index, Vector3 translation) {
  375. if (transformNodes[index].IsAvailable())
  376. transformNodes[index].Translate(translation);
  377. else points[index] += translation;
  378. }
  379. // Calculates the best fitting time in the given interval
  380. private float CPOB(Vector3 aP, float aStart, float aEnd, int aSteps)
  381. {
  382. aStart = Mathf.Clamp01(aStart);
  383. aEnd = Mathf.Clamp01(aEnd);
  384. float step = (aEnd-aStart) / (float)aSteps;
  385. float Res = 0;
  386. float Ref = float.MaxValue;
  387. for (int i = 0; i < aSteps; i++)
  388. {
  389. float t = aStart + step*i;
  390. float L = (GetPoint(t)-aP).sqrMagnitude;
  391. if (L < Ref)
  392. {
  393. Ref = L;
  394. Res = t;
  395. }
  396. }
  397. return Res;
  398. }
  399. public float ClosestTimeFromPoint (Vector3 aP) {
  400. float t = CPOB(aP, 0, 1, 10);
  401. float delta = 1.0f / 10.0f;
  402. for (int i = 0; i < 4; i++)
  403. {
  404. t = CPOB(aP, t - delta, t + delta, 10);
  405. delta /= 9;
  406. }
  407. return t;
  408. }
  409. public Vector3 ClosestPointFromPosition (Vector3 aP) {
  410. return GetPoint(ClosestTimeFromPoint(aP));
  411. }
  412. public Vector3 GetDirection (float t) {
  413. return (GetPoint(t+.001f)-GetPoint(t)).normalized;
  414. }
  415. /// <summary>
  416. /// Adds a node at the last position of the node index.
  417. /// </summary>
  418. public void AddNode () {
  419. AddNode ((points.Count-1)/3);
  420. }
  421. /// <summary>
  422. /// Adds a node at specified node index.
  423. /// </summary>
  424. /// <param name="index">Index.</param>
  425. public void AddNode (int index) {
  426. int nodeIndex = index*3;
  427. Vector3 point = GetPoint(nodeIndex);
  428. Vector3 direction;
  429. if (index>0) {
  430. direction = GetPoint(nodeIndex)-GetPoint(nodeIndex-1);
  431. } else direction = GetPoint(nodeIndex+1)-GetPoint(nodeIndex);
  432. direction*=fixedVelocityOnNewNode;
  433. points.InsertRange(nodeIndex+1, new Vector3[3]);
  434. point += direction;
  435. points[nodeIndex+2] = point;
  436. point += direction;
  437. points[nodeIndex+1] = point;
  438. point += direction;
  439. points[nodeIndex+3] = point;
  440. transformNodes.InsertRange(nodeIndex+1, new TransformNode[]{new TransformNode(), new TransformNode(), new TransformNode()});
  441. BezierControlPointMode currentIndexMode = modes[index];
  442. modes.Insert (index, new BezierControlPointMode());
  443. modes[index] = currentIndexMode;
  444. EnforceMode(index);
  445. SetControlPoint((index+1)*3, GetPoint((index+1)*3));
  446. if (loop) {
  447. points[points.Count - 1] = points[0];
  448. modes[modes.Count - 1] = modes[0];
  449. EnforceMode(0);
  450. }
  451. }
  452. /// <summary>
  453. /// Removes the first node in the node index.
  454. /// </summary>
  455. public void RemoveFirst () {
  456. RemoveNode(0);
  457. }
  458. /// <summary>
  459. /// Removes the last node in the node index.
  460. /// </summary>
  461. public void RemoveLast () {
  462. RemoveNode((points.Count-1)/3);
  463. }
  464. /// <summary>
  465. /// Removes a node at specified node index.
  466. /// </summary>
  467. /// <param name="index">Index.</param>
  468. public void RemoveNode (int index) {
  469. index = Mathf.Clamp (index, 0, points.Count-1);
  470. int pointIndex = index*3;
  471. if (points.Count<=4) return;
  472. if (pointIndex<points.Count-1) {
  473. points.RemoveRange(pointIndex, 3);
  474. transformNodes.RemoveRange(pointIndex, 3);
  475. } else {
  476. points.RemoveRange(pointIndex-2, 3);
  477. transformNodes.RemoveRange(pointIndex-2, 3);
  478. }
  479. modes.RemoveAt (index);
  480. EnforceMode (index-1);
  481. if (index>0)
  482. SetControlPoint((index-1)*3, GetPoint((index-1)*3));
  483. else
  484. SetControlPoint(0, GetPoint(0));
  485. }
  486. /// <summary>
  487. /// Reverses all nodes in the node index.
  488. /// </summary>
  489. public void ReverseAllNodes () {
  490. points.Reverse();
  491. transformNodes.Reverse();
  492. modes.Reverse();
  493. }
  494. public void SwapNodes (int from, int to) {
  495. Vector3[] fromPoints = points.GetRange (from, 3).ToArray();
  496. Vector3[] toPoints = points.GetRange (to, 3).ToArray();
  497. TransformNode[] fromTnode = transformNodes.GetRange (from, 3).ToArray();
  498. TransformNode[] toTnode = transformNodes.GetRange (to, 3).ToArray();
  499. BezierControlPointMode fromMode = modes[from];
  500. BezierControlPointMode toMode = modes[to];
  501. for (int i = from; i<3; i++) {
  502. points[i] = toPoints[i];
  503. transformNodes[i] = toTnode[i];
  504. }
  505. for (int i = to; i<3; i++) {
  506. points[i] = fromPoints[i];
  507. transformNodes[i] = fromTnode[i];
  508. }
  509. modes[from] = toMode;
  510. modes[to] = fromMode;
  511. }
  512. /// <summary>
  513. /// Exports all nodes to Transform[]. Enable exportWithNodeStructure to parent each bezier handle to their node.
  514. /// </summary>
  515. /// <returns>A built-in array of Transforms.</returns>
  516. public Transform[] ExportToTransforms () {
  517. Transform[] transforms = new Transform[points.Count];
  518. for (int i = 0; i<points.Count; i++) {
  519. int iNode = (i+1)/3;
  520. int iBezier = i<3?0:(((i)%3))%2;
  521. bool iIsNode = i==0||i%3==0;
  522. transforms[i] = new GameObject(iIsNode?"Node "+iNode:"Node "+iNode+" - Bezier "+iBezier).transform;
  523. transforms[i].parent = splineTransform;
  524. transforms[i].position = splineTransform.TransformPoint(GetInversePoint(i)+positionOffset);
  525. }
  526. if (exportWithNodeStructure) {
  527. for (int i = 2; i<transforms.Length; i++) {
  528. int iBezier = i<3?0:(((i)%3))%2;
  529. bool iIsNode = i==0||i%3==0;
  530. if (!iIsNode)
  531. transforms[i].parent = transforms[iBezier==0?i+1:i-1];
  532. }
  533. transforms[1].parent = transforms[0];
  534. }
  535. return transforms;
  536. }
  537. /// <summary>
  538. /// Exports all nodes to Vector3[].
  539. /// </summary>
  540. /// <returns>A built-in array of Vector3</returns>
  541. public Vector3[] ExportToVector3 () {
  542. Vector3[] vectors = new Vector3[points.Count];
  543. for (int i = 0; i<points.Count; i++) {
  544. vectors[i] = GetPoint(i)+positionOffset;
  545. }
  546. return vectors;
  547. }
  548. /// <summary>
  549. /// Reset this Playground Spline. Two nodes and two bezier handles will be created.
  550. /// </summary>
  551. public void Reset () {
  552. points = new List<Vector3> {
  553. new Vector3(1f, 0f, 0f),
  554. new Vector3(2f, 0f, 0f),
  555. new Vector3(3f, 0f, 0f),
  556. new Vector3(4f, 0f, 0f)
  557. };
  558. modes = new List<BezierControlPointMode> {
  559. BezierControlPointMode.Aligned,
  560. BezierControlPointMode.Aligned
  561. };
  562. transformNodes = new List<TransformNode> {
  563. new TransformNode(),
  564. new TransformNode(),
  565. new TransformNode(),
  566. new TransformNode()
  567. };
  568. }
  569. /*************************************************************************************************************************************************
  570. MonoBehaviours
  571. *************************************************************************************************************************************************/
  572. void OnEnable () {
  573. isReady = false;
  574. splineTransform = transform;
  575. SetMatrix();
  576. }
  577. void Update () {
  578. SetMatrix();
  579. for (int i = 0; i<transformNodes.Count; i++)
  580. transformNodes[i].Update(splineTransform);
  581. }
  582. void SetMatrix () {
  583. if (previousPosition!=splineTransform.position || previousRotation!=splineTransform.rotation || previousScale!=splineTransform.localScale)
  584. splineTransformMx.SetTRS(splineTransform.position, splineTransform.rotation, splineTransform.localScale);
  585. previousPosition = splineTransform.position;
  586. previousRotation = splineTransform.rotation;
  587. previousScale = splineTransform.localScale;
  588. isReady = true;
  589. }
  590. }
  591. /// <summary>
  592. /// Class for common bezier operations on a spline.
  593. /// </summary>
  594. public static class Bezier {
  595. public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, float t) {
  596. t = Mathf.Clamp01(t);
  597. float oneMinusT = 1f - t;
  598. return
  599. oneMinusT * oneMinusT * p0 +
  600. 2f * oneMinusT * t * p1 +
  601. t * t * p2;
  602. }
  603. public static Vector3 GetFirstDerivative (Vector3 p0, Vector3 p1, Vector3 p2, float t) {
  604. return
  605. 2f * (1f - t) * (p1 - p0) +
  606. 2f * t * (p2 - p1);
  607. }
  608. public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
  609. t = Mathf.Clamp01(t);
  610. float OneMinusT = 1f - t;
  611. return
  612. OneMinusT * OneMinusT * OneMinusT * p0 +
  613. 3f * OneMinusT * OneMinusT * t * p1 +
  614. 3f * OneMinusT * t * t * p2 +
  615. t * t * t * p3;
  616. }
  617. public static Vector3 GetFirstDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
  618. t = Mathf.Clamp01(t);
  619. float oneMinusT = 1f - t;
  620. return
  621. 3f * oneMinusT * oneMinusT * (p1 - p0) +
  622. 6f * oneMinusT * t * (p2 - p1) +
  623. 3f * t * t * (p3 - p2);
  624. }
  625. }
  626. [Serializable]
  627. public class TransformNode {
  628. public bool enabled;
  629. public Transform transform;
  630. bool isAvailable;
  631. Vector3 position;
  632. Vector3 inversePosition;
  633. Vector3 previousPosition;
  634. public bool Update (Transform splineTransform) {
  635. if (enabled && transform!=null) {
  636. previousPosition = position;
  637. position = transform.position;
  638. inversePosition = splineTransform.InverseTransformPoint(transform.position);
  639. isAvailable = true;
  640. return true;
  641. }
  642. isAvailable = false;
  643. return false;
  644. }
  645. public bool IsAvailable () {
  646. return enabled&&isAvailable;
  647. }
  648. public Vector3 GetPosition () {
  649. return position;
  650. }
  651. public Vector3 GetInvsersePosition () {
  652. return inversePosition;
  653. }
  654. public void SetPosition (Vector3 newPosition) {
  655. if (transform==null) return;
  656. transform.position = newPosition;
  657. }
  658. public void Translate (Vector3 translation) {
  659. if (transform==null) return;
  660. transform.position += translation;
  661. }
  662. public Vector3 GetPositionDelta () {
  663. return previousPosition-position;
  664. }
  665. }
  666. public enum SplineMode {
  667. Vector3,
  668. Transform
  669. }
  670. /// <summary>
  671. /// The bezier mode for a spline node. This controls how one bezier handle acts in relation to the other.
  672. /// </summary>
  673. public enum BezierControlPointMode {
  674. /// <summary>
  675. /// Align the angle between the two bezier handles but keep individual lengths. Has a differential smooth in and out angle.
  676. /// </summary>
  677. Aligned,
  678. /// <summary>
  679. /// Align the angle and length between the two bezier handles. Has an equally smooth in and out angle.
  680. /// </summary>
  681. Mirrored,
  682. /// <summary>
  683. /// Bezier handles are freely aligned without consideration to the other. Ables you to have sharp angles.
  684. /// </summary>
  685. Free
  686. }
  687. }