//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// using System; using bs; namespace bs.Editor { /** @addtogroup AnimationEditor * @{ */ /// /// Renders a list of animation events in a form of a timeline. User can set the range of the times to display, /// as well as its physical dimensions. /// public class GUIAnimEvents : GUITimelineBase { private const int EVENT_HALF_WIDTH = 2; private AnimationEvent[] events = new AnimationEvent[0]; private bool[] selectedEvents = new bool[0]; /// /// Constructs a new events timeline and adds it to the specified layout. /// /// Layout to add the events GUI to. /// Width of the GUI element in pixels. /// Height of the GUI element in pixels. public GUIAnimEvents(GUILayout layout, int width, int height) :base(layout, width, height) { } /// /// Attempts to find an event under the provided coordinates. /// /// Coordinates relative to the layout the GUI element is on. /// Index of the event that was clicked on. Index references the events array as provided /// to . Only valid if method returns true. /// True if an event was found under the coordinates, false otherwise. public bool FindEvent(Vector2I pixelCoords, out int eventIdx) { Rect2I bounds = canvas.Bounds; if (pixelCoords.x < (bounds.x + PADDING) || pixelCoords.x >= (bounds.x + bounds.width - PADDING) || pixelCoords.y < bounds.y || pixelCoords.y >= (bounds.y + bounds.height)) { eventIdx = -1; return false; } Vector2I relativeCoords = pixelCoords - new Vector2I(bounds.x, bounds.y); for (int i = 0; i < events.Length; i++) { AnimationEvent evnt = events[i]; int xPos = (int)(((evnt.time - rangeOffset) / GetRange()) * drawableWidth) + PADDING; if (relativeCoords.x >= (xPos - EVENT_HALF_WIDTH) && relativeCoords.x <= (xPos + EVENT_HALF_WIDTH)) { eventIdx = i; return true; } } eventIdx = -1; return false; } /// /// Changes the set of displayed animation events. /// /// Events to display on the timeline. /// Array of the same size as the array, determining which /// events should be displayed as selected. public void SetEvents(AnimationEvent[] events, bool[] selected) { int numEvents; if (events != null) numEvents = events.Length; else numEvents = 0; this.events = new AnimationEvent[numEvents]; if(events != null) Array.Copy(events, this.events, numEvents); selectedEvents = new bool[numEvents]; if(selected != null) Array.Copy(selected, selectedEvents, MathEx.Min(numEvents, selected.Length)); } /// /// Draws a marker for a single event. /// /// Time to draw the marker at. /// If true the marker will be drawn as selected. private void DrawEventMarker(float t, bool selected) { int xPos = (int)(((t - rangeOffset) / GetRange()) * drawableWidth) + PADDING; Vector2I a = new Vector2I(xPos - EVENT_HALF_WIDTH, 0); Vector2I b = new Vector2I(xPos + EVENT_HALF_WIDTH, 0); Vector2I c = new Vector2I(xPos + EVENT_HALF_WIDTH, height - 1); Vector2I d = new Vector2I(xPos - EVENT_HALF_WIDTH, height - 1); // Draw square shape Vector2I[] linePoints = { a, b, c, d, a }; Vector2I[] trianglePoints = { b, c, a, d }; Color outerColor = selected ? Color.BansheeOrange : Color.Black; canvas.DrawTriangleStrip(trianglePoints, Color.White, 101); canvas.DrawPolyLine(linePoints, outerColor, 100); } /// public override void Rebuild() { canvas.Clear(); float range = GetRange(); float lengthPerPixel = rangeLength / drawableWidth; float eventHalfWidth = lengthPerPixel * EVENT_HALF_WIDTH; for (int i = 0; i < events.Length; i++) { float t = events[i].time; float min = t - eventHalfWidth; float max = t + eventHalfWidth; if (max < rangeOffset || min > (rangeOffset + range)) continue; DrawEventMarker(t, selectedEvents[i]); } DrawFrameMarker(); } } /** @} */ }