AnimationWindow.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System.Collections.Generic;
  4. using BansheeEngine;
  5. namespace BansheeEditor
  6. {
  7. /** @addtogroup Windows
  8. * @{
  9. */
  10. /// <summary>
  11. /// Displays animation curve editor window.
  12. /// </summary>
  13. internal class AnimationWindow : EditorWindow
  14. {
  15. public struct KeyframeRef
  16. {
  17. public KeyframeRef(int curveIdx, int keyIdx)
  18. {
  19. this.curveIdx = curveIdx;
  20. this.keyIdx = keyIdx;
  21. }
  22. public int curveIdx;
  23. public int keyIdx;
  24. }
  25. private GUIGraphTime timeline;
  26. private GUICurveDrawing curveDrawing;
  27. private GUIGraphValues sidebar;
  28. private GUIFloatField lengthField;
  29. private GUIIntField fpsField;
  30. private GUIFloatField yRangeField;
  31. private GUIButton addKeyframeBtn;
  32. private GUILayout buttonLayout;
  33. private EdAnimationCurve[] curves = new EdAnimationCurve[0];
  34. private int markedFrameIdx;
  35. private List<KeyframeRef> selectedKeyframes = new List<KeyframeRef>();
  36. private ContextMenu blankContextMenu;
  37. private ContextMenu keyframeContextMenu;
  38. private Vector2I contextClickPosition;
  39. // Keyframe drag
  40. private bool isMousePressedOverKey;
  41. private KeyFrame[] draggedKeyframes;
  42. private Vector2 dragStart;
  43. /// <summary>
  44. /// Opens the animation window.
  45. /// </summary>
  46. [MenuItem("Windows/Animation", ButtonModifier.CtrlAlt, ButtonCode.A, 6000)]
  47. private static void OpenGameWindow()
  48. {
  49. OpenWindow<AnimationWindow>();
  50. }
  51. /// <inheritdoc/>
  52. protected override LocString GetDisplayName()
  53. {
  54. return new LocEdString("Animation");
  55. }
  56. private void OnInitialize()
  57. {
  58. lengthField = new GUIFloatField(new LocEdString("Length"), 50);
  59. fpsField = new GUIIntField(new LocEdString("FPS"), 50);
  60. yRangeField = new GUIFloatField(new LocEdString("Y range"), 50);
  61. addKeyframeBtn = new GUIButton(new LocEdString("Add keyframe"));
  62. lengthField.Value = 60.0f;
  63. fpsField.Value = 1;
  64. yRangeField.Value = 20.0f;
  65. lengthField.OnChanged += x =>
  66. {
  67. timeline.SetRange(lengthField.Value);
  68. curveDrawing.SetRange(lengthField.Value, yRangeField.Value);
  69. };
  70. fpsField.OnChanged += x =>
  71. {
  72. timeline.SetFPS(x);
  73. curveDrawing.SetFPS(x);
  74. };
  75. yRangeField.OnChanged += x =>
  76. {
  77. curveDrawing.SetRange(lengthField.Value, x);
  78. sidebar.SetRange(x * -0.5f, x * 0.5f);
  79. };
  80. addKeyframeBtn.OnClick += () =>
  81. {
  82. AddKeyframeAtMarker();
  83. };
  84. GUILayout mainLayout = GUI.AddLayoutY();
  85. buttonLayout = mainLayout.AddLayoutX();
  86. buttonLayout.AddSpace(5);
  87. buttonLayout.AddElement(lengthField);
  88. buttonLayout.AddSpace(5);
  89. buttonLayout.AddElement(yRangeField);
  90. buttonLayout.AddSpace(5);
  91. buttonLayout.AddElement(fpsField);
  92. buttonLayout.AddSpace(5);
  93. buttonLayout.AddElement(addKeyframeBtn);
  94. buttonLayout.AddSpace(5);
  95. timeline = new GUIGraphTime(mainLayout, Width, 20);
  96. curves = CreateDummyCurves();
  97. curveDrawing = new GUICurveDrawing(mainLayout, Width, Height - 20, curves);
  98. curveDrawing.SetRange(60.0f, 20.0f);
  99. GUIPanel sidebarPanel = GUI.AddPanel(-10);
  100. sidebarPanel.SetPosition(0, 20 + buttonLayout.Bounds.height);
  101. sidebar = new GUIGraphValues(sidebarPanel, 30, Height - 20 - buttonLayout.Bounds.height);
  102. sidebar.SetRange(-10.0f, 10.0f);
  103. curveDrawing.SetSize(Width, Height - 20 - buttonLayout.Bounds.height);
  104. curveDrawing.Rebuild();
  105. blankContextMenu = new ContextMenu();
  106. blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);
  107. blankContextMenu.AddItem("Add event", AddEventAtPosition);
  108. keyframeContextMenu = new ContextMenu();
  109. keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
  110. keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
  111. keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
  112. keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
  113. keyframeContextMenu.AddItem("Tangents/In/Free", () => { ChangeSelectionTangentMode(TangentMode.InFree); });
  114. keyframeContextMenu.AddItem("Tangents/In/Linear", () => { ChangeSelectionTangentMode(TangentMode.InLinear); });
  115. keyframeContextMenu.AddItem("Tangents/In/Step", () => { ChangeSelectionTangentMode(TangentMode.InStep); });
  116. keyframeContextMenu.AddItem("Tangents/Out/Auto", () => { ChangeSelectionTangentMode(TangentMode.OutAuto); });
  117. keyframeContextMenu.AddItem("Tangents/Out/Free", () => { ChangeSelectionTangentMode(TangentMode.OutFree); });
  118. keyframeContextMenu.AddItem("Tangents/Out/Linear", () => { ChangeSelectionTangentMode(TangentMode.OutLinear); });
  119. keyframeContextMenu.AddItem("Tangents/Out/Step", () => { ChangeSelectionTangentMode(TangentMode.OutStep); });
  120. // TODO - Calculate min/max Y and range to set as default
  121. // - Also recalculate whenever curves change and increase as needed
  122. }
  123. private void ChangeSelectionTangentMode(TangentMode mode)
  124. {
  125. // 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)
  126. }
  127. private void AddKeyframeAtPosition()
  128. {
  129. // TODO
  130. }
  131. private void AddEventAtPosition()
  132. {
  133. // TODO
  134. }
  135. private void AddKeyframeAtMarker()
  136. {
  137. ClearSelection();
  138. foreach (var curve in curves)
  139. {
  140. float t = curveDrawing.GetTimeForFrame(markedFrameIdx);
  141. float value = curve.Native.Evaluate(t);
  142. curve.AddKeyframe(t, value);
  143. }
  144. // TODO - UNDOREDO
  145. curveDrawing.Rebuild();
  146. }
  147. private void DeleteSelectedKeyframes()
  148. {
  149. // Sort keys from highest to lowest so they can be removed without changing the indices of the keys
  150. // after them
  151. selectedKeyframes.Sort((x, y) =>
  152. {
  153. if (x.curveIdx.Equals(y.curveIdx))
  154. return y.keyIdx.CompareTo(x.keyIdx);
  155. return x.curveIdx.CompareTo(y.curveIdx);
  156. });
  157. foreach (var keyframe in selectedKeyframes)
  158. curves[keyframe.curveIdx].RemoveKeyframe(keyframe.keyIdx);
  159. // TODO - UNDOREDO
  160. ClearSelection();
  161. curveDrawing.Rebuild();
  162. }
  163. private EdAnimationCurve[] CreateDummyCurves()
  164. {
  165. EdAnimationCurve[] curves = new EdAnimationCurve[1];
  166. curves[0] = new EdAnimationCurve();
  167. curves[0].AddKeyframe(0.0f, 1.0f);
  168. curves[0].AddKeyframe(10.0f, 5.0f);
  169. curves[0].AddKeyframe(15.0f, -2.0f);
  170. curves[0].AddKeyframe(20.0f, 3.0f, TangentMode.InStep);
  171. return curves;
  172. }
  173. protected override void WindowResized(int width, int height)
  174. {
  175. timeline.SetSize(width, 20);
  176. curveDrawing.SetSize(width, height - 20 - buttonLayout.Bounds.height);
  177. sidebar.SetSize(30, height - 20 - buttonLayout.Bounds.height);
  178. curveDrawing.Rebuild();
  179. }
  180. private void ClearSelection()
  181. {
  182. curveDrawing.ClearSelectedKeyframes();
  183. selectedKeyframes.Clear();
  184. isMousePressedOverKey = false;
  185. }
  186. private void SelectKeyframe(int curveIdx, int keyIdx)
  187. {
  188. curveDrawing.SelectKeyframe(curveIdx, keyIdx, true);
  189. if (!IsSelected(curveIdx, keyIdx))
  190. selectedKeyframes.Add(new KeyframeRef(curveIdx, keyIdx));
  191. }
  192. private bool IsSelected(int curveIdx, int keyIdx)
  193. {
  194. int existingIdx = selectedKeyframes.FindIndex(x =>
  195. {
  196. return x.curveIdx == curveIdx && x.keyIdx == keyIdx;
  197. });
  198. return (existingIdx != -1);
  199. }
  200. private void OnEditorUpdate()
  201. {
  202. if (Input.IsPointerButtonDown(PointerButton.Left))
  203. {
  204. Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
  205. Vector2 curveCoord;
  206. int curveIdx;
  207. int keyIdx;
  208. if (curveDrawing.GetCoordInfo(windowPos, out curveCoord, out curveIdx, out keyIdx))
  209. {
  210. if (keyIdx == -1)
  211. ClearSelection();
  212. else
  213. {
  214. if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
  215. ClearSelection();
  216. SelectKeyframe(curveIdx, keyIdx);
  217. isMousePressedOverKey = true;
  218. dragStart = curveCoord;
  219. }
  220. curveDrawing.Rebuild();
  221. }
  222. }
  223. else if (Input.IsPointerButtonHeld(PointerButton.Left))
  224. {
  225. Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
  226. if (isMousePressedOverKey)
  227. {
  228. // TODO - Check if pointer moves some minimal amount
  229. // - If so, start drag. Record all current positions
  230. // - Calculate offset in curve space and apply to all keyframes
  231. }
  232. else
  233. {
  234. int frameIdx = timeline.GetFrame(windowPos);
  235. if (frameIdx != -1)
  236. {
  237. timeline.SetMarkedFrame(frameIdx);
  238. curveDrawing.SetMarkedFrame(frameIdx);
  239. markedFrameIdx = frameIdx;
  240. curveDrawing.Rebuild();
  241. }
  242. }
  243. }
  244. else if (Input.IsPointerButtonUp(PointerButton.Left))
  245. {
  246. isMousePressedOverKey = false;
  247. }
  248. if (Input.IsPointerButtonDown(PointerButton.Right))
  249. {
  250. Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
  251. Vector2 curveCoord;
  252. int curveIdx;
  253. int keyIdx;
  254. if (curveDrawing.GetCoordInfo(windowPos, out curveCoord, out curveIdx, out keyIdx))
  255. {
  256. contextClickPosition = windowPos;
  257. if (keyIdx == -1)
  258. {
  259. ClearSelection();
  260. blankContextMenu.Open(contextClickPosition, GUI);
  261. }
  262. else
  263. {
  264. // If clicked outside of current selection, just select the one keyframe
  265. if (!IsSelected(curveIdx, keyIdx))
  266. {
  267. ClearSelection();
  268. SelectKeyframe(curveIdx, keyIdx);
  269. curveDrawing.Rebuild();
  270. }
  271. keyframeContextMenu.Open(contextClickPosition, GUI);
  272. }
  273. }
  274. }
  275. if(Input.IsButtonUp(ButtonCode.Delete))
  276. DeleteSelectedKeyframes();
  277. }
  278. }
  279. /** @} */
  280. }