GUICurveEditor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using System.Collections.Generic;
  5. using BansheeEngine;
  6. namespace BansheeEditor
  7. {
  8. /** @addtogroup Windows
  9. * @{
  10. */
  11. internal class GUICurveEditor
  12. {
  13. public struct KeyframeRef
  14. {
  15. public KeyframeRef(int curveIdx, int keyIdx)
  16. {
  17. this.curveIdx = curveIdx;
  18. this.keyIdx = keyIdx;
  19. }
  20. public int curveIdx;
  21. public int keyIdx;
  22. }
  23. private const int TIMELINE_HEIGHT = 20;
  24. private const int SIDEBAR_WIDTH = 30;
  25. private GUILayout gui;
  26. private GUIPanel drawingPanel;
  27. private GUIGraphTime guiTimeline;
  28. private GUICurveDrawing guiCurveDrawing;
  29. private GUIGraphValues guiSidebar;
  30. private ContextMenu blankContextMenu;
  31. private ContextMenu keyframeContextMenu;
  32. private Vector2I contextClickPosition;
  33. private EdAnimationCurve[] curves = new EdAnimationCurve[0];
  34. private int markedFrameIdx;
  35. private List<KeyframeRef> selectedKeyframes = new List<KeyframeRef>();
  36. private bool isMousePressedOverKey;
  37. private KeyFrame[] draggedKeyframes;
  38. private Vector2 dragStart;
  39. public GUICurveEditor(GUILayout gui, int width, int height)
  40. {
  41. this.gui = gui;
  42. blankContextMenu = new ContextMenu();
  43. blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);
  44. blankContextMenu.AddItem("Add event", AddEventAtPosition);
  45. keyframeContextMenu = new ContextMenu();
  46. keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
  47. keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
  48. keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
  49. keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
  50. keyframeContextMenu.AddItem("Tangents/In/Free", () => { ChangeSelectionTangentMode(TangentMode.InFree); });
  51. keyframeContextMenu.AddItem("Tangents/In/Linear", () => { ChangeSelectionTangentMode(TangentMode.InLinear); });
  52. keyframeContextMenu.AddItem("Tangents/In/Step", () => { ChangeSelectionTangentMode(TangentMode.InStep); });
  53. keyframeContextMenu.AddItem("Tangents/Out/Auto", () => { ChangeSelectionTangentMode(TangentMode.OutAuto); });
  54. keyframeContextMenu.AddItem("Tangents/Out/Free", () => { ChangeSelectionTangentMode(TangentMode.OutFree); });
  55. keyframeContextMenu.AddItem("Tangents/Out/Linear", () => { ChangeSelectionTangentMode(TangentMode.OutLinear); });
  56. keyframeContextMenu.AddItem("Tangents/Out/Step", () => { ChangeSelectionTangentMode(TangentMode.OutStep); });
  57. guiTimeline = new GUIGraphTime(gui, width, TIMELINE_HEIGHT);
  58. drawingPanel = gui.AddPanel();
  59. drawingPanel.SetPosition(0, TIMELINE_HEIGHT);
  60. guiCurveDrawing = new GUICurveDrawing(drawingPanel, width, height - TIMELINE_HEIGHT, curves);
  61. guiCurveDrawing.SetRange(60.0f, 20.0f);
  62. GUIPanel sidebarPanel = gui.AddPanel(-10);
  63. sidebarPanel.SetPosition(0, TIMELINE_HEIGHT);
  64. guiSidebar = new GUIGraphValues(sidebarPanel, SIDEBAR_WIDTH, height - TIMELINE_HEIGHT);
  65. guiSidebar.SetRange(-10.0f, 10.0f);
  66. }
  67. /// <summary>
  68. /// Change the set of curves to display.
  69. /// </summary>
  70. /// <param name="curves">New set of curves to draw on the GUI element.</param>
  71. public void SetCurves(EdAnimationCurve[] curves)
  72. {
  73. this.curves = curves;
  74. guiCurveDrawing.SetCurves(curves);
  75. // TODO - Recalculate valid size
  76. Redraw();
  77. }
  78. /// <summary>
  79. /// Change the physical size of the GUI element.
  80. /// </summary>
  81. /// <param name="width">Width of the element in pixels.</param>
  82. /// <param name="height">Height of the element in pixels.</param>
  83. public void SetSize(int width, int height)
  84. {
  85. guiTimeline.SetSize(width, TIMELINE_HEIGHT);
  86. guiCurveDrawing.SetSize(width, height - TIMELINE_HEIGHT);
  87. guiSidebar.SetSize(SIDEBAR_WIDTH, height - TIMELINE_HEIGHT);
  88. Redraw();
  89. }
  90. /// <summary>
  91. /// Changes the visible range that the GUI element displays.
  92. /// </summary>
  93. /// <param name="xRange">Range of the horizontal area. Displayed area will range from [0, xRange].</param>
  94. /// <param name="yRange">Range of the vertical area. Displayed area will range from
  95. /// [-yRange * 0.5, yRange * 0.5]</param>
  96. public void SetRange(float xRange, float yRange)
  97. {
  98. guiTimeline.SetRange(xRange);
  99. guiCurveDrawing.SetRange(xRange, yRange);
  100. guiSidebar.SetRange(yRange * -0.5f, yRange * 0.5f);
  101. Redraw();
  102. }
  103. /// <summary>
  104. /// Number of frames per second, used for frame selection and marking.
  105. /// </summary>
  106. /// <param name="fps">Number of prames per second.</param>
  107. public void SetFPS(int fps)
  108. {
  109. guiTimeline.SetFPS(fps);
  110. guiCurveDrawing.SetFPS(fps);
  111. Redraw();
  112. }
  113. /// <summary>
  114. /// Sets the frame at which to display the frame marker.
  115. /// </summary>
  116. /// <param name="frameIdx">Index of the frame to display the marker on, or -1 to clear the marker.</param>
  117. public void SetMarkedFrame(int frameIdx)
  118. {
  119. markedFrameIdx = frameIdx;
  120. guiTimeline.SetMarkedFrame(frameIdx);
  121. guiCurveDrawing.SetMarkedFrame(frameIdx);
  122. Redraw();
  123. }
  124. public void AddKeyFrameAtMarker()
  125. {
  126. ClearSelection();
  127. foreach (var curve in curves)
  128. {
  129. float t = guiCurveDrawing.GetTimeForFrame(markedFrameIdx);
  130. float value = curve.Native.Evaluate(t);
  131. curve.AddKeyframe(t, value);
  132. }
  133. // TODO - UNDOREDO
  134. guiCurveDrawing.Rebuild();
  135. }
  136. public void Redraw()
  137. {
  138. guiCurveDrawing.Rebuild();
  139. guiTimeline.Rebuild();
  140. guiSidebar.Rebuild();
  141. }
  142. public void HandleInput(Vector2I pointerPos)
  143. {
  144. Rect2I drawingBounds = drawingPanel.Bounds;
  145. Vector2I drawingPos = pointerPos - new Vector2I(drawingBounds.x, drawingBounds.y);
  146. if (Input.IsPointerButtonDown(PointerButton.Left))
  147. {
  148. Vector2 curveCoord;
  149. int curveIdx;
  150. int keyIdx;
  151. if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
  152. {
  153. if (keyIdx == -1)
  154. ClearSelection();
  155. else
  156. {
  157. if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
  158. ClearSelection();
  159. SelectKeyframe(curveIdx, keyIdx);
  160. isMousePressedOverKey = true;
  161. dragStart = curveCoord;
  162. }
  163. guiCurveDrawing.Rebuild();
  164. }
  165. }
  166. if (Input.IsPointerButtonHeld(PointerButton.Left))
  167. {
  168. if (isMousePressedOverKey)
  169. {
  170. // TODO - Check if pointer moves some minimal amount
  171. // - If so, start drag. Record all current positions
  172. // - Calculate offset in curve space and apply to all keyframes
  173. }
  174. else
  175. {
  176. int frameIdx = guiTimeline.GetFrame(pointerPos);
  177. if (frameIdx != -1)
  178. SetMarkedFrame(frameIdx);
  179. }
  180. }
  181. if (Input.IsPointerButtonUp(PointerButton.Left))
  182. {
  183. isMousePressedOverKey = false;
  184. }
  185. if (Input.IsPointerButtonDown(PointerButton.Right))
  186. {
  187. Vector2 curveCoord;
  188. int curveIdx;
  189. int keyIdx;
  190. if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
  191. {
  192. contextClickPosition = pointerPos;
  193. if (keyIdx == -1)
  194. {
  195. ClearSelection();
  196. blankContextMenu.Open(contextClickPosition, gui);
  197. }
  198. else
  199. {
  200. // If clicked outside of current selection, just select the one keyframe
  201. if (!IsSelected(curveIdx, keyIdx))
  202. {
  203. ClearSelection();
  204. SelectKeyframe(curveIdx, keyIdx);
  205. guiCurveDrawing.Rebuild();
  206. }
  207. keyframeContextMenu.Open(contextClickPosition, gui);
  208. }
  209. }
  210. }
  211. if (Input.IsButtonUp(ButtonCode.Delete))
  212. DeleteSelectedKeyframes();
  213. }
  214. private void ChangeSelectionTangentMode(TangentMode mode)
  215. {
  216. // TODO - When changing just left or right, make sure to persist opposite tangent mode (or switch to equivalent broken mode if it wasn't broken)
  217. }
  218. private void AddKeyframeAtPosition()
  219. {
  220. // TODO
  221. }
  222. private void AddEventAtPosition()
  223. {
  224. // TODO
  225. }
  226. private void DeleteSelectedKeyframes()
  227. {
  228. // Sort keys from highest to lowest so they can be removed without changing the indices of the keys
  229. // after them
  230. selectedKeyframes.Sort((x, y) =>
  231. {
  232. if (x.curveIdx.Equals(y.curveIdx))
  233. return y.keyIdx.CompareTo(x.keyIdx);
  234. return x.curveIdx.CompareTo(y.curveIdx);
  235. });
  236. foreach (var keyframe in selectedKeyframes)
  237. curves[keyframe.curveIdx].RemoveKeyframe(keyframe.keyIdx);
  238. // TODO - UNDOREDO
  239. ClearSelection();
  240. guiCurveDrawing.Rebuild();
  241. }
  242. private void ClearSelection()
  243. {
  244. guiCurveDrawing.ClearSelectedKeyframes();
  245. selectedKeyframes.Clear();
  246. isMousePressedOverKey = false;
  247. }
  248. private void SelectKeyframe(int curveIdx, int keyIdx)
  249. {
  250. guiCurveDrawing.SelectKeyframe(curveIdx, keyIdx, true);
  251. if (!IsSelected(curveIdx, keyIdx))
  252. selectedKeyframes.Add(new GUICurveEditor.KeyframeRef(curveIdx, keyIdx));
  253. }
  254. private bool IsSelected(int curveIdx, int keyIdx)
  255. {
  256. int existingIdx = selectedKeyframes.FindIndex(x =>
  257. {
  258. return x.curveIdx == curveIdx && x.keyIdx == keyIdx;
  259. });
  260. return (existingIdx != -1);
  261. }
  262. }
  263. /** @} */
  264. }