GUIAnimEvents.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using BansheeEngine;
  5. namespace BansheeEditor
  6. {
  7. /** @addtogroup AnimationEditor
  8. * @{
  9. */
  10. /// <summary>
  11. /// Renders a list of animation events in a form of a timeline. User can set the range of the times to display,
  12. /// as well as its physical dimensions.
  13. /// </summary>
  14. public class GUIAnimEvents
  15. {
  16. private const int EVENT_HALF_WIDTH = 2;
  17. private float rangeLength = 60.0f;
  18. private float rangeOffset = 0.0f;
  19. private int fps = 1;
  20. private GUICanvas canvas;
  21. private int width;
  22. private int height;
  23. private int drawableWidth;
  24. private AnimationEvent[] events = new AnimationEvent[0];
  25. private bool[] selectedEvents = new bool[0];
  26. /// <summary>
  27. /// Constructs a new events timeline and adds it to the specified layout.
  28. /// </summary>
  29. /// <param name="layout">Layout to add the events GUI to.</param>
  30. /// <param name="width">Width of the GUI element in pixels.</param>
  31. /// <param name="height">Height of the GUI element in pixels.</param>
  32. public GUIAnimEvents(GUILayout layout, int width, int height)
  33. {
  34. canvas = new GUICanvas();
  35. layout.AddElement(canvas);
  36. SetSize(width, height);
  37. }
  38. /// <summary>
  39. /// Attempts to find an event under the provided coordinates.
  40. /// </summary>
  41. /// <param name="pixelCoords">Coordinates relative to the layout the GUI element is on.</param>
  42. /// <param name="eventIdx">Index of the event that was clicked on. Index references the events array as provided
  43. /// to <see cref="SetEvents"/>. Only valid if method returns true.</param>
  44. /// <returns>True if an event was found under the coordinates, false otherwise.</returns>
  45. public bool FindEvent(Vector2I pixelCoords, out int eventIdx)
  46. {
  47. Rect2I bounds = canvas.Bounds;
  48. if (pixelCoords.x < (bounds.x + GUIGraphTime.PADDING) || pixelCoords.x >= (bounds.x + bounds.width - GUIGraphTime.PADDING) ||
  49. pixelCoords.y < bounds.y || pixelCoords.y >= (bounds.y + bounds.height))
  50. {
  51. eventIdx = -1;
  52. return false;
  53. }
  54. Vector2I relativeCoords = pixelCoords - new Vector2I(bounds.x + GUIGraphTime.PADDING, bounds.y);
  55. for (int i = 0; i < events.Length; i++)
  56. {
  57. AnimationEvent evnt = events[i];
  58. int xPos = (int)(((evnt.Time - rangeOffset) / GetRange()) * drawableWidth) + GUIGraphTime.PADDING;
  59. if (relativeCoords.x >= (xPos - EVENT_HALF_WIDTH) || relativeCoords.y >= (xPos + EVENT_HALF_WIDTH))
  60. {
  61. eventIdx = i;
  62. return true;
  63. }
  64. }
  65. eventIdx = -1;
  66. return false;
  67. }
  68. /// <summary>
  69. /// Sets the physical size of the GUI element.
  70. /// </summary>
  71. /// <param name="width">Width in pixels.</param>
  72. /// <param name="height">Height in pixels.</param>
  73. public void SetSize(int width, int height)
  74. {
  75. this.width = width;
  76. this.height = height;
  77. canvas.SetWidth(width);
  78. canvas.SetHeight(height);
  79. drawableWidth = Math.Max(0, width - GUIGraphTime.PADDING * 2);
  80. }
  81. /// <summary>
  82. /// Sets the range of values over which to display the events.
  83. /// </summary>
  84. /// <param name="length">Amount of time to display, in seconds.</param>
  85. public void SetRange(float length)
  86. {
  87. rangeLength = Math.Max(0.0f, length);
  88. }
  89. /// <summary>
  90. /// Returns the offset at which the displayed event values start at.
  91. /// </summary>
  92. /// <param name="offset">Value to start displaying the events at, in seconds.</param>
  93. public void SetOffset(float offset)
  94. {
  95. rangeOffset = offset;
  96. }
  97. /// <summary>
  98. /// Number of frames per second, used for rounding up the displayed range.
  99. /// </summary>
  100. /// <param name="fps">Number of prames per second.</param>
  101. public void SetFPS(int fps)
  102. {
  103. this.fps = Math.Max(1, fps);
  104. }
  105. /// <summary>
  106. /// Changes the set of displayed animation events.
  107. /// </summary>
  108. /// <param name="events">Events to display on the timeline.</param>
  109. /// <param name="selected">Array of the same size as the <paramref name="events"/> array, determining which
  110. /// events should be displayed as selected.</param>
  111. public void SetEvents(AnimationEvent[] events, bool[] selected)
  112. {
  113. int numEvents;
  114. if (events != null)
  115. numEvents = events.Length;
  116. else
  117. numEvents = 0;
  118. this.events = new AnimationEvent[numEvents];
  119. if(events != null)
  120. Array.Copy(events, this.events, numEvents);
  121. selectedEvents = new bool[numEvents];
  122. if(selected != null)
  123. Array.Copy(selected, selectedEvents, MathEx.Min(numEvents, selected.Length));
  124. }
  125. /// <summary>
  126. /// Draws a marker for a single event.
  127. /// </summary>
  128. /// <param name="t">Time to draw the marker at.</param>
  129. /// <param name="selected">If true the marker will be drawn as selected.</param>
  130. private void DrawEventMarker(float t, bool selected)
  131. {
  132. int xPos = (int)(((t - rangeOffset) / GetRange()) * drawableWidth) + GUIGraphTime.PADDING;
  133. Vector2I a = new Vector2I(xPos - EVENT_HALF_WIDTH, height - 1);
  134. Vector2I b = new Vector2I(xPos, 0);
  135. Vector2I c = new Vector2I(xPos + EVENT_HALF_WIDTH, height - 1);
  136. Vector2I d = new Vector2I(xPos, 0);
  137. // Draw square shape
  138. Vector2I[] linePoints = { a, b, c, d, a };
  139. Vector2I[] trianglePoints = { b, c, a, d };
  140. Color outerColor = selected ? Color.BansheeOrange : Color.Black;
  141. canvas.DrawTriangleStrip(trianglePoints, Color.White, 101);
  142. canvas.DrawPolyLine(linePoints, outerColor, 100);
  143. }
  144. /// <summary>
  145. /// Returns the range of times displayed by the timeline rounded to the multiple of FPS.
  146. /// </summary>
  147. /// <param name="padding">If true, extra range will be included to cover the right-most padding.</param>
  148. /// <returns>Time range rounded to a multiple of FPS.</returns>
  149. private float GetRange(bool padding = false)
  150. {
  151. float spf = 1.0f / fps;
  152. float range = rangeLength;
  153. if (padding)
  154. {
  155. float lengthPerPixel = rangeLength / drawableWidth;
  156. range += lengthPerPixel * GUIGraphTime.PADDING;
  157. }
  158. return ((int)range / spf) * spf;
  159. }
  160. /// <summary>
  161. /// Rebuilds the internal GUI elements. Should be called whenever timeline properties change.
  162. /// </summary>
  163. public void Rebuild()
  164. {
  165. canvas.Clear();
  166. float range = GetRange();
  167. float lengthPerPixel = rangeLength / drawableWidth;
  168. float eventHalfWidth = lengthPerPixel * EVENT_HALF_WIDTH;
  169. for (int i = 0; i < events.Length; i++)
  170. {
  171. float t = events[i].Time;
  172. float min = t - eventHalfWidth;
  173. float max = t + eventHalfWidth;
  174. if (max < rangeOffset || min > (rangeOffset + range))
  175. continue;
  176. DrawEventMarker(t, selectedEvents[i]);
  177. }
  178. }
  179. }
  180. /** @} */
  181. }