AnimationWindow.cs 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411
  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 System.Text;
  6. using BansheeEngine;
  7. namespace BansheeEditor
  8. {
  9. /** @addtogroup Windows
  10. * @{
  11. */
  12. /// <summary>
  13. /// Displays animation curve editor window.
  14. /// </summary>
  15. [DefaultSize(900, 500)]
  16. internal class AnimationWindow : EditorWindow
  17. {
  18. private const int FIELD_DISPLAY_WIDTH = 200;
  19. private const int DRAG_START_DISTANCE = 3;
  20. private const float DRAG_SCALE = 10.0f;
  21. private const float ZOOM_SCALE = 0.1f/120.0f; // One scroll step is usually 120 units, we want 1/10 of that
  22. private bool isInitialized;
  23. private SceneObject selectedSO;
  24. #region Overrides
  25. /// <summary>
  26. /// Opens the animation window.
  27. /// </summary>
  28. [MenuItem("Windows/Animation", ButtonModifier.CtrlAlt, ButtonCode.A, 6000)]
  29. private static void OpenGameWindow()
  30. {
  31. OpenWindow<AnimationWindow>();
  32. }
  33. /// <inheritdoc/>
  34. protected override LocString GetDisplayName()
  35. {
  36. return new LocEdString("Animation");
  37. }
  38. private void OnInitialize()
  39. {
  40. Selection.OnSelectionChanged += OnSelectionChanged;
  41. EditorInput.OnPointerPressed += OnPointerPressed;
  42. EditorInput.OnPointerMoved += OnPointerMoved;
  43. EditorInput.OnPointerReleased += OnPointerReleased;
  44. EditorInput.OnButtonUp += OnButtonUp;
  45. RebuildGUI();
  46. }
  47. private void OnEditorUpdate()
  48. {
  49. if (!isInitialized)
  50. return;
  51. HandleDragAndZoomInput();
  52. }
  53. private void OnDestroy()
  54. {
  55. Selection.OnSelectionChanged -= OnSelectionChanged;
  56. EditorInput.OnPointerPressed -= OnPointerPressed;
  57. EditorInput.OnPointerMoved -= OnPointerMoved;
  58. EditorInput.OnPointerReleased -= OnPointerReleased;
  59. EditorInput.OnButtonUp -= OnButtonUp;
  60. }
  61. protected override void WindowResized(int width, int height)
  62. {
  63. if (!isInitialized)
  64. return;
  65. ResizeGUI(width, height);
  66. }
  67. #endregion
  68. #region GUI
  69. private GUIButton playButton;
  70. private GUIButton recordButton;
  71. private GUIButton prevFrameButton;
  72. private GUIIntField frameInputField;
  73. private GUIButton nextFrameButton;
  74. private GUIButton addKeyframeButton;
  75. private GUIButton addEventButton;
  76. private GUIButton optionsButton;
  77. private GUIButton addPropertyBtn;
  78. private GUIButton delPropertyBtn;
  79. private GUILayout buttonLayout;
  80. private int buttonLayoutHeight;
  81. private int scrollBarWidth;
  82. private int scrollBarHeight;
  83. private GUIResizeableScrollBarH horzScrollBar;
  84. private GUIResizeableScrollBarV vertScrollBar;
  85. private GUIPanel editorPanel;
  86. private GUIAnimFieldDisplay guiFieldDisplay;
  87. private GUICurveEditor guiCurveEditor;
  88. private void RebuildGUI()
  89. {
  90. GUI.Clear();
  91. selectedFields.Clear();
  92. curves.Clear();
  93. isInitialized = false;
  94. if (selectedSO != Selection.SceneObject)
  95. {
  96. zoomAmount = 0.0f;
  97. selectedSO = Selection.SceneObject;
  98. }
  99. if (selectedSO == null)
  100. {
  101. GUILabel warningLbl = new GUILabel(new LocEdString("Select an object to animate in the Hierarchy or Scene windows."));
  102. GUILayoutY vertLayout = GUI.AddLayoutY();
  103. vertLayout.AddFlexibleSpace();
  104. GUILayoutX horzLayout = vertLayout.AddLayoutX();
  105. vertLayout.AddFlexibleSpace();
  106. horzLayout.AddFlexibleSpace();
  107. horzLayout.AddElement(warningLbl);
  108. horzLayout.AddFlexibleSpace();
  109. return;
  110. }
  111. // TODO - Retrieve Animation & AnimationClip from the selected object, fill curves dictionary
  112. // - If not available, show a button to create new animation clip
  113. // Top button row
  114. GUIContent playIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.Play),
  115. new LocEdString("Play"));
  116. GUIContent recordIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.Record),
  117. new LocEdString("Record"));
  118. GUIContent prevFrameIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.FrameBack),
  119. new LocEdString("Previous frame"));
  120. GUIContent nextFrameIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.FrameForward),
  121. new LocEdString("Next frame"));
  122. GUIContent addKeyframeIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.AddKeyframe),
  123. new LocEdString("Add keyframe"));
  124. GUIContent addEventIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.AddEvent),
  125. new LocEdString("Add event"));
  126. GUIContent optionsIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Options),
  127. new LocEdString("Options"));
  128. playButton = new GUIButton(playIcon);
  129. recordButton = new GUIButton(recordIcon);
  130. prevFrameButton = new GUIButton(prevFrameIcon);
  131. frameInputField = new GUIIntField();
  132. nextFrameButton = new GUIButton(nextFrameIcon);
  133. addKeyframeButton = new GUIButton(addKeyframeIcon);
  134. addEventButton = new GUIButton(addEventIcon);
  135. optionsButton = new GUIButton(optionsIcon);
  136. playButton.OnClick += () =>
  137. {
  138. // TODO
  139. // - Record current state of the scene object hierarchy
  140. // - Evaluate all curves manually and update them
  141. // - On end, restore original values of the scene object hierarchy
  142. };
  143. recordButton.OnClick += () =>
  144. {
  145. // TODO
  146. // - Every frame read back current values of all the current curve's properties and assign it to the current frame
  147. };
  148. prevFrameButton.OnClick += () =>
  149. {
  150. SetCurrentFrame(currentFrameIdx - 1);
  151. };
  152. frameInputField.OnChanged += SetCurrentFrame;
  153. nextFrameButton.OnClick += () =>
  154. {
  155. SetCurrentFrame(currentFrameIdx + 1);
  156. };
  157. addKeyframeButton.OnClick += () =>
  158. {
  159. guiCurveEditor.AddKeyFrameAtMarker();
  160. };
  161. addEventButton.OnClick += () =>
  162. {
  163. guiCurveEditor.AddEventAtMarker();
  164. };
  165. optionsButton.OnClick += () =>
  166. {
  167. Vector2I openPosition = ScreenToWindowPos(Input.PointerPosition);
  168. AnimationOptions dropDown = DropDownWindow.Open<AnimationOptions>(this, openPosition);
  169. dropDown.Initialize(this);
  170. };
  171. // Property buttons
  172. addPropertyBtn = new GUIButton(new LocEdString("Add property"));
  173. delPropertyBtn = new GUIButton(new LocEdString("Delete selected"));
  174. addPropertyBtn.OnClick += () =>
  175. {
  176. Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
  177. FieldSelectionWindow fieldSelection = DropDownWindow.Open<FieldSelectionWindow>(this, windowPos);
  178. fieldSelection.OnFieldSelected += OnFieldAdded;
  179. };
  180. delPropertyBtn.OnClick += () =>
  181. {
  182. LocEdString title = new LocEdString("Warning");
  183. LocEdString message = new LocEdString("Are you sure you want to remove all selected fields?");
  184. DialogBox.Open(title, message, DialogBox.Type.YesNo, x =>
  185. {
  186. if (x == DialogBox.ResultType.Yes)
  187. {
  188. RemoveSelectedFields();
  189. }
  190. });
  191. };
  192. GUILayout mainLayout = GUI.AddLayoutY();
  193. buttonLayout = mainLayout.AddLayoutX();
  194. buttonLayout.AddSpace(5);
  195. buttonLayout.AddElement(playButton);
  196. buttonLayout.AddElement(recordButton);
  197. buttonLayout.AddSpace(5);
  198. buttonLayout.AddElement(prevFrameButton);
  199. buttonLayout.AddElement(frameInputField);
  200. buttonLayout.AddElement(nextFrameButton);
  201. buttonLayout.AddSpace(5);
  202. buttonLayout.AddElement(addKeyframeButton);
  203. buttonLayout.AddElement(addEventButton);
  204. buttonLayout.AddSpace(5);
  205. buttonLayout.AddElement(optionsButton);
  206. buttonLayout.AddFlexibleSpace();
  207. buttonLayoutHeight = playButton.Bounds.height;
  208. GUILayout contentLayout = mainLayout.AddLayoutX();
  209. GUILayout fieldDisplayLayout = contentLayout.AddLayoutY(GUIOption.FixedWidth(FIELD_DISPLAY_WIDTH));
  210. guiFieldDisplay = new GUIAnimFieldDisplay(fieldDisplayLayout, FIELD_DISPLAY_WIDTH,
  211. Height - buttonLayoutHeight * 2, selectedSO);
  212. guiFieldDisplay.OnEntrySelected += OnFieldSelected;
  213. GUILayout bottomButtonLayout = fieldDisplayLayout.AddLayoutX();
  214. bottomButtonLayout.AddElement(addPropertyBtn);
  215. bottomButtonLayout.AddElement(delPropertyBtn);
  216. horzScrollBar = new GUIResizeableScrollBarH();
  217. horzScrollBar.OnScrollOrResize += OnHorzScrollOrResize;
  218. vertScrollBar = new GUIResizeableScrollBarV();
  219. vertScrollBar.OnScrollOrResize += OnVertScrollOrResize;
  220. GUILayout curveLayout = contentLayout.AddLayoutY();
  221. GUILayout curveLayoutHorz = curveLayout.AddLayoutX();
  222. GUILayout horzScrollBarLayout = curveLayout.AddLayoutX();
  223. horzScrollBarLayout.AddElement(horzScrollBar);
  224. horzScrollBarLayout.AddFlexibleSpace();
  225. editorPanel = curveLayoutHorz.AddPanel();
  226. curveLayoutHorz.AddElement(vertScrollBar);
  227. curveLayoutHorz.AddFlexibleSpace();
  228. scrollBarHeight = horzScrollBar.Bounds.height;
  229. scrollBarWidth = vertScrollBar.Bounds.width;
  230. Vector2I curveEditorSize = GetCurveEditorSize();
  231. guiCurveEditor = new GUICurveEditor(this, editorPanel, curveEditorSize.x, curveEditorSize.y);
  232. guiCurveEditor.OnFrameSelected += OnFrameSelected;
  233. guiCurveEditor.Redraw();
  234. horzScrollBar.SetWidth(curveEditorSize.x);
  235. vertScrollBar.SetHeight(curveEditorSize.y);
  236. SetCurrentFrame(currentFrameIdx);
  237. UpdateScrollBarSize();
  238. isInitialized = true;
  239. }
  240. private void ResizeGUI(int width, int height)
  241. {
  242. guiFieldDisplay.SetSize(FIELD_DISPLAY_WIDTH, height - buttonLayoutHeight * 2);
  243. Vector2I curveEditorSize = GetCurveEditorSize();
  244. guiCurveEditor.SetSize(curveEditorSize.x, curveEditorSize.y);
  245. guiCurveEditor.Redraw();
  246. horzScrollBar.SetWidth(curveEditorSize.x);
  247. vertScrollBar.SetHeight(curveEditorSize.y);
  248. UpdateScrollBarSize();
  249. UpdateScrollBarPosition();
  250. }
  251. #endregion
  252. #region Scroll, drag, zoom
  253. private Vector2I dragStartPos;
  254. private bool isButtonHeld;
  255. private bool isDragInProgress;
  256. private float zoomAmount;
  257. private void HandleDragAndZoomInput()
  258. {
  259. // Handle middle mouse dragging
  260. if (isDragInProgress)
  261. {
  262. float dragX = Input.GetAxisValue(InputAxis.MouseX) * DRAG_SCALE;
  263. float dragY = Input.GetAxisValue(InputAxis.MouseY) * DRAG_SCALE;
  264. Vector2 offset = guiCurveEditor.Offset;
  265. offset.x = Math.Max(0.0f, offset.x + dragX);
  266. offset.y += dragY;
  267. guiCurveEditor.Offset = offset;
  268. UpdateScrollBarSize();
  269. UpdateScrollBarPosition();
  270. }
  271. // Handle zoom in/out
  272. float scroll = Input.GetAxisValue(InputAxis.MouseZ);
  273. if (scroll != 0.0f)
  274. {
  275. Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
  276. Vector2 curvePos;
  277. if (guiCurveEditor.WindowToCurveSpace(windowPos, out curvePos))
  278. {
  279. float zoom = scroll * ZOOM_SCALE;
  280. Zoom(curvePos, zoom);
  281. }
  282. }
  283. }
  284. private void SetVertScrollbarProperties(float position, float size)
  285. {
  286. Vector2 visibleRange = guiCurveEditor.Range;
  287. Vector2 totalRange = GetTotalRange();
  288. visibleRange.y = totalRange.y*size;
  289. guiCurveEditor.Range = visibleRange;
  290. float scrollableRange = totalRange.y - visibleRange.y;
  291. Vector2 offset = guiCurveEditor.Offset;
  292. offset.y = -scrollableRange * (position * 2.0f - 1.0f);
  293. guiCurveEditor.Offset = offset;
  294. }
  295. private void SetHorzScrollbarProperties(float position, float size)
  296. {
  297. Vector2 visibleRange = guiCurveEditor.Range;
  298. Vector2 totalRange = GetTotalRange();
  299. visibleRange.x = totalRange.x * size;
  300. guiCurveEditor.Range = visibleRange;
  301. float scrollableRange = totalRange.x - visibleRange.x;
  302. Vector2 offset = guiCurveEditor.Offset;
  303. offset.x = scrollableRange * position;
  304. guiCurveEditor.Offset = offset;
  305. }
  306. private void UpdateScrollBarSize()
  307. {
  308. Vector2 visibleRange = guiCurveEditor.Range;
  309. Vector2 totalRange = GetTotalRange();
  310. horzScrollBar.HandleSize = visibleRange.x / totalRange.x;
  311. vertScrollBar.HandleSize = visibleRange.y / totalRange.y;
  312. }
  313. private void UpdateScrollBarPosition()
  314. {
  315. Vector2 visibleRange = guiCurveEditor.Range;
  316. Vector2 totalRange = GetTotalRange();
  317. Vector2 scrollableRange = totalRange - visibleRange;
  318. Vector2 offset = guiCurveEditor.Offset;
  319. if (scrollableRange.x > 0.0f)
  320. horzScrollBar.Position = offset.x / scrollableRange.x;
  321. else
  322. horzScrollBar.Position = 0.0f;
  323. if (scrollableRange.y > 0.0f)
  324. {
  325. float pos = offset.y/scrollableRange.y;
  326. float sign = MathEx.Sign(pos);
  327. pos = sign*MathEx.Clamp01(MathEx.Abs(pos));
  328. pos = (1.0f - pos) /2.0f;
  329. vertScrollBar.Position = pos;
  330. }
  331. else
  332. vertScrollBar.Position = 0.0f;
  333. }
  334. private Vector2 GetZoomedRange()
  335. {
  336. float zoomLevel = MathEx.Pow(2, zoomAmount);
  337. Vector2 optimalRange = GetOptimalRange();
  338. return optimalRange / zoomLevel;
  339. }
  340. private Vector2 GetTotalRange()
  341. {
  342. // Return optimal range (that covers the visible curve)
  343. Vector2 optimalRange = GetOptimalRange();
  344. // Increase range in case user zoomed out
  345. Vector2 zoomedRange = GetZoomedRange();
  346. return Vector2.Max(optimalRange, zoomedRange);
  347. }
  348. private void Zoom(Vector2 curvePos, float amount)
  349. {
  350. // Increase or decrease the visible range depending on zoom level
  351. Vector2 oldZoomedRange = GetZoomedRange();
  352. zoomAmount = MathEx.Clamp(zoomAmount + amount, -10.0f, 10.0f);
  353. Vector2 zoomedRange = GetZoomedRange();
  354. Vector2 zoomedDiff = zoomedRange - oldZoomedRange;
  355. Vector2 currentRange = guiCurveEditor.Range;
  356. Vector2 newRange = currentRange + zoomedDiff;
  357. guiCurveEditor.Range = newRange;
  358. // When zooming, make sure to focus on the point provided, so adjust the offset
  359. Vector2 rangeScale = newRange;
  360. rangeScale.x /= currentRange.x;
  361. rangeScale.y /= currentRange.y;
  362. Vector2 relativeCurvePos = curvePos - guiCurveEditor.Offset;
  363. Vector2 newCurvePos = relativeCurvePos * rangeScale;
  364. Vector2 diff = newCurvePos - relativeCurvePos;
  365. guiCurveEditor.Offset -= diff;
  366. UpdateScrollBarSize();
  367. UpdateScrollBarPosition();
  368. }
  369. #endregion
  370. #region Curve save/load
  371. /// <summary>
  372. /// A set of animation curves for a field of a certain type.
  373. /// </summary>
  374. private struct FieldCurves
  375. {
  376. public SerializableProperty.FieldType type;
  377. public EdAnimationCurve[] curves;
  378. }
  379. [SerializeObject]
  380. private class EditorVector3CurveTangents
  381. {
  382. public string name;
  383. public TangentMode[] tangentsX;
  384. public TangentMode[] tangentsY;
  385. public TangentMode[] tangentsZ;
  386. }
  387. [SerializeObject]
  388. private class EditorFloatCurveTangents
  389. {
  390. public string name;
  391. public TangentMode[] tangents;
  392. }
  393. [SerializeObject]
  394. private class EditorCurveData
  395. {
  396. public EditorVector3CurveTangents[] positionCurves;
  397. public EditorVector3CurveTangents[] rotationCurves;
  398. public EditorVector3CurveTangents[] scaleCurves;
  399. public EditorFloatCurveTangents[] floatCurves;
  400. }
  401. private Dictionary<string, FieldCurves> curves = new Dictionary<string, FieldCurves>();
  402. private bool clipIsImported;
  403. private void LoadFromClip(AnimationClip clip)
  404. {
  405. curves.Clear();
  406. selectedFields.Clear();
  407. guiFieldDisplay.SetFields(new string[0]);
  408. clipIsImported = IsClipImported(clip);
  409. AnimationCurves clipCurves = clip.Curves;
  410. EditorCurveData editorCurveData = null;
  411. string resourcePath = ProjectLibrary.GetPath(clip);
  412. if (!string.IsNullOrEmpty(resourcePath))
  413. {
  414. LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
  415. string clipName = PathEx.GetTail(resourcePath);
  416. if (entry != null && entry.Type == LibraryEntryType.File)
  417. {
  418. FileEntry fileEntry = (FileEntry)entry;
  419. ResourceMeta[] metas = fileEntry.ResourceMetas;
  420. for (int i = 0; i < metas.Length; i++)
  421. {
  422. if (clipName == metas[i].SubresourceName)
  423. {
  424. editorCurveData = metas[i].EditorData as EditorCurveData;
  425. break;
  426. }
  427. }
  428. }
  429. }
  430. if(editorCurveData == null)
  431. editorCurveData = new EditorCurveData();
  432. Action<NamedVector3Curve[], EditorVector3CurveTangents[], string> loadVector3Curve =
  433. (curves, tangents, subPath) =>
  434. {
  435. foreach (var curveEntry in curves)
  436. {
  437. TangentMode[] tangentsX = null;
  438. TangentMode[] tangentsY = null;
  439. TangentMode[] tangentsZ = null;
  440. foreach (var tangentEntry in tangents)
  441. {
  442. if (tangentEntry.name == curveEntry.Name)
  443. {
  444. tangentsX = tangentEntry.tangentsX;
  445. tangentsY = tangentEntry.tangentsY;
  446. tangentsZ = tangentEntry.tangentsZ;
  447. break;
  448. }
  449. }
  450. FieldCurves fieldCurves = new FieldCurves();
  451. fieldCurves.type = SerializableProperty.FieldType.Vector3;
  452. fieldCurves.curves = new EdAnimationCurve[3];
  453. fieldCurves.curves[0] = new EdAnimationCurve(curveEntry.X, tangentsX);
  454. fieldCurves.curves[1] = new EdAnimationCurve(curveEntry.Y, tangentsY);
  455. fieldCurves.curves[2] = new EdAnimationCurve(curveEntry.Z, tangentsZ);
  456. string curvePath = curveEntry.Name.TrimEnd('/') + subPath;
  457. guiFieldDisplay.AddField(curvePath);
  458. this.curves[curvePath] = fieldCurves;
  459. }
  460. };
  461. loadVector3Curve(clipCurves.PositionCurves, editorCurveData.positionCurves, "/Position");
  462. loadVector3Curve(clipCurves.RotationCurves, editorCurveData.rotationCurves, "/Rotation");
  463. loadVector3Curve(clipCurves.ScaleCurves, editorCurveData.scaleCurves, "/Scale");
  464. // Find which individual float curves belong to the same field
  465. Dictionary<string, Tuple<int, bool>> suffixToIdxMapping = new Dictionary<string, Tuple<int, bool>>();
  466. suffixToIdxMapping[".x"] = Tuple.Create(0, true);
  467. suffixToIdxMapping[".y"] = Tuple.Create(1, true);
  468. suffixToIdxMapping[".z"] = Tuple.Create(2, true);
  469. suffixToIdxMapping[".w"] = Tuple.Create(3, true);
  470. suffixToIdxMapping[".r"] = Tuple.Create(0, false);
  471. suffixToIdxMapping[".g"] = Tuple.Create(1, false);
  472. suffixToIdxMapping[".b"] = Tuple.Create(2, false);
  473. suffixToIdxMapping[".a"] = Tuple.Create(3, false);
  474. Dictionary<string, Tuple<int, int, bool>[]> floatCurveMapping = new Dictionary<string, Tuple<int, int, bool>[]>();
  475. {
  476. int curveIdx = 0;
  477. foreach (var curveEntry in clipCurves.FloatCurves)
  478. {
  479. string path = curveEntry.Name;
  480. string pathNoSuffix = null;
  481. string pathSuffix;
  482. if (path.Length >= 2)
  483. {
  484. pathSuffix = path.Substring(path.Length - 2, 2);
  485. pathNoSuffix = path.Substring(0, path.Length - 2);
  486. }
  487. else
  488. pathSuffix = "";
  489. int tangentIdx = -1;
  490. int currentTangentIdx = 0;
  491. foreach (var tangentEntry in editorCurveData.floatCurves)
  492. {
  493. if (tangentEntry.name == curveEntry.Name)
  494. {
  495. tangentIdx = currentTangentIdx;
  496. break;
  497. }
  498. currentTangentIdx++;
  499. }
  500. Tuple<int, bool> suffixInfo;
  501. if (suffixToIdxMapping.TryGetValue(pathSuffix, out suffixInfo))
  502. {
  503. Tuple<int, int, bool>[] curveInfo;
  504. if (!floatCurveMapping.TryGetValue(pathNoSuffix, out curveInfo))
  505. curveInfo = new Tuple<int, int, bool>[4];
  506. curveInfo[suffixInfo.Item1] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.Item2);
  507. floatCurveMapping[pathNoSuffix] = curveInfo;
  508. }
  509. else
  510. {
  511. Tuple<int, int, bool>[] curveInfo = new Tuple<int, int, bool>[4];
  512. curveInfo[0] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.Item2);
  513. floatCurveMapping[path] = curveInfo;
  514. }
  515. curveIdx++;
  516. }
  517. }
  518. foreach (var KVP in floatCurveMapping)
  519. {
  520. int numCurves = 0;
  521. for (int i = 0; i < 4; i++)
  522. {
  523. if (KVP.Value[i] == null)
  524. continue;
  525. numCurves++;
  526. }
  527. if (numCurves == 0)
  528. continue; // Invalid curve
  529. FieldCurves fieldCurves = new FieldCurves();
  530. // Deduce type (note that all single value types are assumed to be float even if their source type is int or bool)
  531. if (numCurves == 1)
  532. fieldCurves.type = SerializableProperty.FieldType.Float;
  533. else if (numCurves == 2)
  534. fieldCurves.type = SerializableProperty.FieldType.Vector2;
  535. else if (numCurves == 3)
  536. fieldCurves.type = SerializableProperty.FieldType.Vector3;
  537. else // 4 curves
  538. {
  539. bool isVector = KVP.Value[0].Item3;
  540. if (isVector)
  541. fieldCurves.type = SerializableProperty.FieldType.Vector4;
  542. else
  543. fieldCurves.type = SerializableProperty.FieldType.Color;
  544. }
  545. fieldCurves.curves = new EdAnimationCurve[numCurves];
  546. for (int i = 0; i < numCurves; i++)
  547. {
  548. int curveIdx = KVP.Value[i].Item1;
  549. int tangentIdx = KVP.Value[i].Item2;
  550. TangentMode[] tangents = null;
  551. if (tangentIdx != -1)
  552. tangents = editorCurveData.floatCurves[tangentIdx].tangents;
  553. fieldCurves.curves[i] = new EdAnimationCurve(clipCurves.FloatCurves[curveIdx].Curve, tangents);
  554. }
  555. string curvePath = KVP.Key;
  556. guiFieldDisplay.AddField(curvePath);
  557. curves[curvePath] = fieldCurves;
  558. }
  559. // Add events
  560. guiCurveEditor.Events = clip.Events;
  561. }
  562. private void SaveToClip(AnimationClip clip, string saveToPath)
  563. {
  564. if (!clipIsImported)
  565. {
  566. List<NamedVector3Curve> positionCurves = new List<NamedVector3Curve>();
  567. List<NamedVector3Curve> rotationCurves = new List<NamedVector3Curve>();
  568. List<NamedVector3Curve> scaleCurves = new List<NamedVector3Curve>();
  569. List<NamedFloatCurve> floatCurves = new List<NamedFloatCurve>();
  570. List<EditorVector3CurveTangents> positionTangents = new List<EditorVector3CurveTangents>();
  571. List<EditorVector3CurveTangents> rotationTangents = new List<EditorVector3CurveTangents>();
  572. List<EditorVector3CurveTangents> scaleTangents = new List<EditorVector3CurveTangents>();
  573. List<EditorFloatCurveTangents> floatTangents = new List<EditorFloatCurveTangents>();
  574. foreach (var kvp in curves)
  575. {
  576. string[] pathEntries = kvp.Key.Split('/');
  577. if (pathEntries.Length == 0)
  578. continue;
  579. string lastEntry = pathEntries[pathEntries.Length - 1];
  580. if (lastEntry == "Position" || lastEntry == "Rotation" || lastEntry == "Scale")
  581. {
  582. StringBuilder sb = new StringBuilder();
  583. for (int i = 0; i < pathEntries.Length - 2; i++)
  584. sb.Append(pathEntries[i] + "/");
  585. if (pathEntries.Length > 1)
  586. sb.Append(pathEntries[pathEntries.Length - 2]);
  587. string curvePath = sb.ToString();
  588. NamedVector3Curve curve = new NamedVector3Curve(curvePath,
  589. new AnimationCurve(kvp.Value.curves[0].KeyFrames),
  590. new AnimationCurve(kvp.Value.curves[1].KeyFrames),
  591. new AnimationCurve(kvp.Value.curves[2].KeyFrames));
  592. EditorVector3CurveTangents tangents = new EditorVector3CurveTangents();
  593. tangents.name = curvePath;
  594. tangents.tangentsX = kvp.Value.curves[0].TangentModes;
  595. tangents.tangentsY = kvp.Value.curves[1].TangentModes;
  596. tangents.tangentsZ = kvp.Value.curves[2].TangentModes;
  597. if (lastEntry == "Position")
  598. {
  599. positionCurves.Add(curve);
  600. positionTangents.Add(tangents);
  601. }
  602. else if (lastEntry == "Rotation")
  603. {
  604. rotationCurves.Add(curve);
  605. rotationTangents.Add(tangents);
  606. }
  607. else if (lastEntry == "Scale")
  608. {
  609. scaleCurves.Add(curve);
  610. scaleTangents.Add(tangents);
  611. }
  612. }
  613. else
  614. {
  615. Action<int, string> addCurve = (idx, subPath) =>
  616. {
  617. string path = kvp.Key + subPath;
  618. NamedFloatCurve curve = new NamedFloatCurve(path,
  619. new AnimationCurve(kvp.Value.curves[idx].KeyFrames));
  620. EditorFloatCurveTangents tangents = new EditorFloatCurveTangents();
  621. tangents.name = path;
  622. tangents.tangents = kvp.Value.curves[idx].TangentModes;
  623. floatCurves.Add(curve);
  624. floatTangents.Add(tangents);
  625. };
  626. switch (kvp.Value.type)
  627. {
  628. case SerializableProperty.FieldType.Vector2:
  629. addCurve(0, ".x");
  630. addCurve(1, ".y");
  631. break;
  632. case SerializableProperty.FieldType.Vector3:
  633. addCurve(0, ".x");
  634. addCurve(1, ".y");
  635. addCurve(2, ".z");
  636. break;
  637. case SerializableProperty.FieldType.Vector4:
  638. addCurve(0, ".x");
  639. addCurve(1, ".y");
  640. addCurve(2, ".z");
  641. addCurve(3, ".w");
  642. break;
  643. case SerializableProperty.FieldType.Color:
  644. addCurve(0, ".r");
  645. addCurve(1, ".g");
  646. addCurve(2, ".b");
  647. addCurve(3, ".a");
  648. break;
  649. case SerializableProperty.FieldType.Bool:
  650. case SerializableProperty.FieldType.Int:
  651. case SerializableProperty.FieldType.Float:
  652. addCurve(0, "");
  653. break;
  654. }
  655. }
  656. }
  657. AnimationCurves newClipCurves = new AnimationCurves();
  658. newClipCurves.PositionCurves = positionCurves.ToArray();
  659. newClipCurves.RotationCurves = rotationCurves.ToArray();
  660. newClipCurves.ScaleCurves = scaleCurves.ToArray();
  661. newClipCurves.FloatCurves = floatCurves.ToArray();
  662. clip.Curves = newClipCurves;
  663. clip.Events = guiCurveEditor.Events;
  664. string resourcePath = ProjectLibrary.GetPath(clip);
  665. if (string.IsNullOrEmpty(resourcePath))
  666. ProjectLibrary.Create(clip, saveToPath);
  667. else
  668. ProjectLibrary.Save(clip);
  669. // Save tangents for editor only use
  670. LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
  671. string clipName = PathEx.GetTail(resourcePath);
  672. if (entry != null && entry.Type == LibraryEntryType.File)
  673. {
  674. FileEntry fileEntry = (FileEntry)entry;
  675. ResourceMeta[] metas = fileEntry.ResourceMetas;
  676. for (int i = 0; i < metas.Length; i++)
  677. {
  678. if (clipName == metas[i].SubresourceName)
  679. {
  680. EditorCurveData newCurveData = new EditorCurveData();
  681. newCurveData.positionCurves = positionTangents.ToArray();
  682. newCurveData.rotationCurves = rotationTangents.ToArray();
  683. newCurveData.scaleCurves = scaleTangents.ToArray();
  684. newCurveData.floatCurves = floatTangents.ToArray();
  685. ProjectLibrary.SetEditorData(resourcePath, newCurveData);
  686. break;
  687. }
  688. }
  689. }
  690. }
  691. else
  692. {
  693. string resourcePath = ProjectLibrary.GetPath(clip);
  694. LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
  695. if (entry != null && entry.Type == LibraryEntryType.File)
  696. {
  697. FileEntry fileEntry = (FileEntry) entry;
  698. MeshImportOptions meshImportOptions = (MeshImportOptions)fileEntry.Options;
  699. string clipName = PathEx.GetTail(resourcePath);
  700. List<ImportedAnimationEvents> newEvents = new List<ImportedAnimationEvents>();
  701. newEvents.AddRange(meshImportOptions.AnimationEvents);
  702. bool isExisting = false;
  703. for (int i = 0; i < newEvents.Count; i++)
  704. {
  705. if (newEvents[i].name == clipName)
  706. {
  707. newEvents[i].events = guiCurveEditor.Events;
  708. isExisting = true;
  709. break;
  710. }
  711. }
  712. if (!isExisting)
  713. {
  714. ImportedAnimationEvents newEntry = new ImportedAnimationEvents();
  715. newEntry.name = clipName;
  716. newEntry.events = guiCurveEditor.Events;
  717. newEvents.Add(newEntry);
  718. }
  719. meshImportOptions.AnimationEvents = newEvents.ToArray();
  720. ProjectLibrary.Reimport(resourcePath, meshImportOptions, true);
  721. }
  722. }
  723. }
  724. private bool IsClipImported(AnimationClip clip)
  725. {
  726. string resourcePath = ProjectLibrary.GetPath(clip);
  727. return ProjectLibrary.IsSubresource(resourcePath);
  728. }
  729. #endregion
  730. #region Curve display
  731. private int currentFrameIdx;
  732. private int fps = 1;
  733. internal int FPS
  734. {
  735. get { return fps; }
  736. set { guiCurveEditor.SetFPS(value); fps = MathEx.Max(value, 1); }
  737. }
  738. private void SetCurrentFrame(int frameIdx)
  739. {
  740. currentFrameIdx = Math.Max(0, frameIdx);
  741. frameInputField.Value = currentFrameIdx;
  742. guiCurveEditor.SetMarkedFrame(currentFrameIdx);
  743. float time = guiCurveEditor.GetTimeForFrame(currentFrameIdx);
  744. List<GUIAnimFieldPathValue> values = new List<GUIAnimFieldPathValue>();
  745. foreach (var kvp in curves)
  746. {
  747. SerializableProperty property = GUIAnimFieldDisplay.FindProperty(selectedSO, kvp.Key);
  748. if (property != null)
  749. {
  750. GUIAnimFieldPathValue fieldValue = new GUIAnimFieldPathValue();
  751. fieldValue.path = kvp.Key;
  752. switch (kvp.Value.type)
  753. {
  754. case SerializableProperty.FieldType.Vector2:
  755. {
  756. Vector2 value = new Vector2();
  757. for (int i = 0; i < 2; i++)
  758. value[i] = kvp.Value.curves[i].Evaluate(time, false);
  759. fieldValue.value = value;
  760. }
  761. break;
  762. case SerializableProperty.FieldType.Vector3:
  763. {
  764. Vector3 value = new Vector3();
  765. for (int i = 0; i < 3; i++)
  766. value[i] = kvp.Value.curves[i].Evaluate(time, false);
  767. fieldValue.value = value;
  768. }
  769. break;
  770. case SerializableProperty.FieldType.Vector4:
  771. {
  772. Vector4 value = new Vector4();
  773. for (int i = 0; i < 4; i++)
  774. value[i] = kvp.Value.curves[i].Evaluate(time, false);
  775. fieldValue.value = value;
  776. }
  777. break;
  778. case SerializableProperty.FieldType.Color:
  779. {
  780. Color value = new Color();
  781. for (int i = 0; i < 4; i++)
  782. value[i] = kvp.Value.curves[i].Evaluate(time, false);
  783. fieldValue.value = value;
  784. }
  785. break;
  786. case SerializableProperty.FieldType.Bool:
  787. case SerializableProperty.FieldType.Int:
  788. case SerializableProperty.FieldType.Float:
  789. fieldValue.value = kvp.Value.curves[0].Evaluate(time, false); ;
  790. break;
  791. }
  792. values.Add(fieldValue);
  793. }
  794. }
  795. guiFieldDisplay.SetDisplayValues(values.ToArray());
  796. }
  797. private Vector2 GetOptimalRange()
  798. {
  799. List<EdAnimationCurve> displayedCurves = new List<EdAnimationCurve>();
  800. for (int i = 0; i < selectedFields.Count; i++)
  801. {
  802. EdAnimationCurve curve;
  803. if (TryGetCurve(selectedFields[i], out curve))
  804. displayedCurves.Add(curve);
  805. }
  806. float xRange;
  807. float yRange;
  808. CalculateRange(displayedCurves, out xRange, out yRange);
  809. // Add padding to y range
  810. yRange *= 1.05f;
  811. // Don't allow zero range
  812. if (xRange == 0.0f)
  813. xRange = 60.0f;
  814. if (yRange == 0.0f)
  815. yRange = 10.0f;
  816. return new Vector2(xRange, yRange);
  817. }
  818. private void UpdateDisplayedCurves()
  819. {
  820. List<EdAnimationCurve> curvesToDisplay = new List<EdAnimationCurve>();
  821. for (int i = 0; i < selectedFields.Count; i++)
  822. {
  823. EdAnimationCurve curve;
  824. if (TryGetCurve(selectedFields[i], out curve))
  825. curvesToDisplay.Add(curve);
  826. }
  827. guiCurveEditor.SetCurves(curvesToDisplay.ToArray());
  828. Vector2 newRange = GetOptimalRange();
  829. // Don't reduce visible range
  830. newRange.x = Math.Max(newRange.x, guiCurveEditor.Range.x);
  831. newRange.y = Math.Max(newRange.y, guiCurveEditor.Range.y);
  832. guiCurveEditor.Range = newRange;
  833. UpdateScrollBarSize();
  834. }
  835. #endregion
  836. #region Field display
  837. private List<string> selectedFields = new List<string>();
  838. private void AddNewField(string path, SerializableProperty.FieldType type)
  839. {
  840. guiFieldDisplay.AddField(path);
  841. switch (type)
  842. {
  843. case SerializableProperty.FieldType.Vector4:
  844. {
  845. FieldCurves fieldCurves = new FieldCurves();
  846. fieldCurves.type = type;
  847. fieldCurves.curves = new EdAnimationCurve[4];
  848. string[] subPaths = { ".x", ".y", ".z", ".w" };
  849. for (int i = 0; i < subPaths.Length; i++)
  850. {
  851. string subFieldPath = path + subPaths[i];
  852. fieldCurves.curves[i] = new EdAnimationCurve();
  853. selectedFields.Add(subFieldPath);
  854. }
  855. curves[path] = fieldCurves;
  856. }
  857. break;
  858. case SerializableProperty.FieldType.Vector3:
  859. {
  860. FieldCurves fieldCurves = new FieldCurves();
  861. fieldCurves.type = type;
  862. fieldCurves.curves = new EdAnimationCurve[3];
  863. string[] subPaths = { ".x", ".y", ".z" };
  864. for (int i = 0; i < subPaths.Length; i++)
  865. {
  866. string subFieldPath = path + subPaths[i];
  867. fieldCurves.curves[i] = new EdAnimationCurve();
  868. selectedFields.Add(subFieldPath);
  869. }
  870. curves[path] = fieldCurves;
  871. }
  872. break;
  873. case SerializableProperty.FieldType.Vector2:
  874. {
  875. FieldCurves fieldCurves = new FieldCurves();
  876. fieldCurves.type = type;
  877. fieldCurves.curves = new EdAnimationCurve[2];
  878. string[] subPaths = { ".x", ".y" };
  879. for (int i = 0; i < subPaths.Length; i++)
  880. {
  881. string subFieldPath = path + subPaths[i];
  882. fieldCurves.curves[i] = new EdAnimationCurve();
  883. selectedFields.Add(subFieldPath);
  884. }
  885. curves[path] = fieldCurves;
  886. }
  887. break;
  888. case SerializableProperty.FieldType.Color:
  889. {
  890. FieldCurves fieldCurves = new FieldCurves();
  891. fieldCurves.type = type;
  892. fieldCurves.curves = new EdAnimationCurve[4];
  893. string[] subPaths = { ".r", ".g", ".b", ".a" };
  894. for (int i = 0; i < subPaths.Length; i++)
  895. {
  896. string subFieldPath = path + subPaths[i];
  897. fieldCurves.curves[i] = new EdAnimationCurve();
  898. selectedFields.Add(subFieldPath);
  899. }
  900. curves[path] = fieldCurves;
  901. }
  902. break;
  903. default: // Primitive type
  904. {
  905. FieldCurves fieldCurves = new FieldCurves();
  906. fieldCurves.type = type;
  907. fieldCurves.curves = new EdAnimationCurve[1];
  908. fieldCurves.curves[0] = new EdAnimationCurve();
  909. selectedFields.Add(path);
  910. curves[path] = fieldCurves;
  911. }
  912. break;
  913. }
  914. UpdateDisplayedCurves();
  915. }
  916. private void SelectField(string path, bool additive)
  917. {
  918. if (!additive)
  919. selectedFields.Clear();
  920. if (!string.IsNullOrEmpty(path))
  921. {
  922. selectedFields.RemoveAll(x => { return x == path || IsPathParent(x, path); });
  923. selectedFields.Add(path);
  924. }
  925. guiFieldDisplay.SetSelection(selectedFields.ToArray());
  926. UpdateDisplayedCurves();
  927. }
  928. private void RemoveSelectedFields()
  929. {
  930. for (int i = 0; i < selectedFields.Count; i++)
  931. {
  932. selectedFields.Remove(selectedFields[i]);
  933. curves.Remove(GetSubPathParent(selectedFields[i]));
  934. }
  935. List<string> existingFields = new List<string>();
  936. foreach (var KVP in curves)
  937. existingFields.Add(KVP.Key);
  938. guiFieldDisplay.SetFields(existingFields.ToArray());
  939. selectedFields.Clear();
  940. UpdateDisplayedCurves();
  941. }
  942. #endregion
  943. #region Helpers
  944. private Vector2I GetCurveEditorSize()
  945. {
  946. Vector2I output = new Vector2I();
  947. output.x = Math.Max(0, Width - FIELD_DISPLAY_WIDTH - scrollBarWidth);
  948. output.y = Math.Max(0, Height - buttonLayoutHeight - scrollBarHeight);
  949. return output;
  950. }
  951. private static void CalculateRange(List<EdAnimationCurve> curves, out float xRange, out float yRange)
  952. {
  953. xRange = 0.0f;
  954. yRange = 0.0f;
  955. foreach (var curve in curves)
  956. {
  957. KeyFrame[] keyframes = curve.KeyFrames;
  958. foreach (var key in keyframes)
  959. {
  960. xRange = Math.Max(xRange, key.time);
  961. yRange = Math.Max(yRange, Math.Abs(key.value));
  962. }
  963. }
  964. }
  965. private bool TryGetCurve(string path, out EdAnimationCurve curve)
  966. {
  967. int index = path.LastIndexOf(".");
  968. string parentPath;
  969. string subPathSuffix = null;
  970. if (index == -1)
  971. {
  972. parentPath = path;
  973. }
  974. else
  975. {
  976. parentPath = path.Substring(0, index);
  977. subPathSuffix = path.Substring(index, path.Length - index);
  978. }
  979. FieldCurves fieldCurves;
  980. if (curves.TryGetValue(parentPath, out fieldCurves))
  981. {
  982. if (!string.IsNullOrEmpty(subPathSuffix))
  983. {
  984. if (subPathSuffix == ".x" || subPathSuffix == ".r")
  985. {
  986. curve = fieldCurves.curves[0];
  987. return true;
  988. }
  989. else if (subPathSuffix == ".y" || subPathSuffix == ".g")
  990. {
  991. curve = fieldCurves.curves[1];
  992. return true;
  993. }
  994. else if (subPathSuffix == ".z" || subPathSuffix == ".b")
  995. {
  996. curve = fieldCurves.curves[2];
  997. return true;
  998. }
  999. else if (subPathSuffix == ".w" || subPathSuffix == ".a")
  1000. {
  1001. curve = fieldCurves.curves[3];
  1002. return true;
  1003. }
  1004. }
  1005. else
  1006. {
  1007. curve = fieldCurves.curves[0];
  1008. return true;
  1009. }
  1010. }
  1011. curve = null;
  1012. return false;
  1013. }
  1014. private bool IsPathParent(string child, string parent)
  1015. {
  1016. string[] childEntries = child.Split('/', '.');
  1017. string[] parentEntries = parent.Split('/', '.');
  1018. if (parentEntries.Length >= child.Length)
  1019. return false;
  1020. int compareLength = Math.Min(childEntries.Length, parentEntries.Length);
  1021. for (int i = 0; i < compareLength; i++)
  1022. {
  1023. if (childEntries[i] != parentEntries[i])
  1024. return false;
  1025. }
  1026. return true;
  1027. }
  1028. private string GetSubPathParent(string path)
  1029. {
  1030. int index = path.LastIndexOf(".");
  1031. if (index == -1)
  1032. return path;
  1033. return path.Substring(0, index);
  1034. }
  1035. #endregion
  1036. #region Input callbacks
  1037. private void OnPointerPressed(PointerEvent ev)
  1038. {
  1039. if (!isInitialized)
  1040. return;
  1041. guiCurveEditor.OnPointerPressed(ev);
  1042. if (ev.button == PointerButton.Middle)
  1043. {
  1044. Vector2I windowPos = ScreenToWindowPos(ev.ScreenPos);
  1045. Vector2 curvePos;
  1046. if (guiCurveEditor.WindowToCurveSpace(windowPos, out curvePos))
  1047. {
  1048. dragStartPos = windowPos;
  1049. isButtonHeld = true;
  1050. }
  1051. }
  1052. }
  1053. private void OnPointerMoved(PointerEvent ev)
  1054. {
  1055. if (!isInitialized)
  1056. return;
  1057. guiCurveEditor.OnPointerMoved(ev);
  1058. if (isButtonHeld)
  1059. {
  1060. Vector2I windowPos = ScreenToWindowPos(ev.ScreenPos);
  1061. int distance = Vector2I.Distance(dragStartPos, windowPos);
  1062. if (distance >= DRAG_START_DISTANCE)
  1063. {
  1064. isDragInProgress = true;
  1065. Cursor.Hide();
  1066. Rect2I clipRect;
  1067. clipRect.x = ev.ScreenPos.x - 2;
  1068. clipRect.y = ev.ScreenPos.y - 2;
  1069. clipRect.width = 4;
  1070. clipRect.height = 4;
  1071. Cursor.ClipToRect(clipRect);
  1072. }
  1073. }
  1074. }
  1075. private void OnPointerReleased(PointerEvent ev)
  1076. {
  1077. if (isDragInProgress)
  1078. {
  1079. Cursor.Show();
  1080. Cursor.ClipDisable();
  1081. }
  1082. isButtonHeld = false;
  1083. isDragInProgress = false;
  1084. if (!isInitialized)
  1085. return;
  1086. guiCurveEditor.OnPointerReleased(ev);
  1087. }
  1088. private void OnButtonUp(ButtonEvent ev)
  1089. {
  1090. if (!isInitialized)
  1091. return;
  1092. guiCurveEditor.OnButtonUp(ev);
  1093. }
  1094. #endregion
  1095. #region General callbacks
  1096. private void OnFieldAdded(string path, SerializableProperty.FieldType type)
  1097. {
  1098. AddNewField(path, type);
  1099. }
  1100. private void OnHorzScrollOrResize(float position, float size)
  1101. {
  1102. SetHorzScrollbarProperties(position, size);
  1103. }
  1104. private void OnVertScrollOrResize(float position, float size)
  1105. {
  1106. SetVertScrollbarProperties(position, size);
  1107. }
  1108. private void OnFieldSelected(string path)
  1109. {
  1110. bool additive = Input.IsButtonHeld(ButtonCode.LeftShift) || Input.IsButtonHeld(ButtonCode.RightShift);
  1111. SelectField(path, additive);
  1112. }
  1113. private void OnSelectionChanged(SceneObject[] sceneObjects, string[] resourcePaths)
  1114. {
  1115. RebuildGUI();
  1116. }
  1117. private void OnFrameSelected(int frameIdx)
  1118. {
  1119. SetCurrentFrame(frameIdx);
  1120. }
  1121. #endregion
  1122. }
  1123. /// <summary>
  1124. /// Drop down window that displays options used by the animation window.
  1125. /// </summary>
  1126. [DefaultSize(100, 50)]
  1127. internal class AnimationOptions : DropDownWindow
  1128. {
  1129. /// <summary>
  1130. /// Initializes the drop down window by creating the necessary GUI. Must be called after construction and before
  1131. /// use.
  1132. /// </summary>
  1133. /// <param name="parent">Animation window that this drop down window is a part of.</param>
  1134. internal void Initialize(AnimationWindow parent)
  1135. {
  1136. GUIIntField fpsField = new GUIIntField(new LocEdString("FPS"), 40);
  1137. fpsField.Value = parent.FPS;
  1138. fpsField.OnChanged += x => { parent.FPS = x; };
  1139. GUILayoutY vertLayout = GUI.AddLayoutY();
  1140. vertLayout.AddFlexibleSpace();
  1141. GUILayoutX contentLayout = vertLayout.AddLayoutX();
  1142. contentLayout.AddFlexibleSpace();
  1143. contentLayout.AddElement(fpsField);
  1144. contentLayout.AddFlexibleSpace();
  1145. vertLayout.AddFlexibleSpace();
  1146. }
  1147. }
  1148. /** @} */
  1149. }