Explorar o código

GUITimeline WIP

BearishSun %!s(int64=9) %!d(string=hai) anos
pai
achega
039172a958

+ 182 - 49
Source/MBansheeEditor/Windows/Animation/GUITimeline.cs

@@ -16,7 +16,7 @@ namespace BansheeEditor
         private const float SMALL_TICK_HEIGHT_PCT = 0.2f;
         private const float SMALL_TICK_HEIGHT_PCT = 0.2f;
         private const int PADDING = 30;
         private const int PADDING = 30;
         private const int TEXT_PADDING = 2;
         private const int TEXT_PADDING = 2;
-        private const int MIN_TICK_DISTANCE = 10;
+        private const int MIN_TICK_DISTANCE = 25;
 
 
         private GUICanvas canvas;
         private GUICanvas canvas;
         private int width;
         private int width;
@@ -59,78 +59,211 @@ namespace BansheeEditor
             Rebuild();
             Rebuild();
         }
         }
 
 
+        private float CalcTickInterval(float length)
+        {
+            const int OPTIMAL_TICK_COUNT = 10;
+            float[] validIntervals =
+            {
+                0.001f, 0.005f, 0.010f, 0.025f, 0.050f, 0.100f, 0.250f, 0.500f, // Hundreds of a second
+                1.0f, 5.0f, 10.0f, 30.0f, // Seconds
+                60.0f, 120.0f, 300.0f, 600.0f, 1800.0f, 3600.0f // Minutes
+            };
+
+            int bestIntervalIdx = 0;
+            float bestDistance = float.MaxValue;
+
+            for(int i = 0; i < validIntervals.Length; i++)
+            {
+                float tickCount = length/validIntervals[i];
+                float distance = Math.Abs(tickCount - OPTIMAL_TICK_COUNT);
+                if (distance < bestDistance)
+                {
+                    bestDistance = distance;
+                    bestIntervalIdx = i;
+                }
+            }
+
+            // If the draw area is too narrow, limit amount of ticks displayed so they aren't all clumped together
+            int numTicks = MathEx.FloorToInt(length / validIntervals[bestIntervalIdx]) + 1;
+
+            int drawableWidth = Math.Max(0, width - PADDING * 2);
+            int spacePerTick = drawableWidth/numTicks;
+
+            float bestInterval;
+            if (spacePerTick < MIN_TICK_DISTANCE)
+            {
+                int maxTickCount = drawableWidth/MIN_TICK_DISTANCE;
+
+                bool foundInterval = false;
+                for (int i = bestIntervalIdx; i < validIntervals.Length; i++)
+                {
+                    float tickCount = length/validIntervals[i];
+                    if (tickCount <= maxTickCount)
+                    {
+                        bestIntervalIdx = i;
+                        foundInterval = true;
+                        break;
+                    }
+                }
+
+                // Haven't found a valid round interval, try more intervals
+                if (!foundInterval)
+                {
+                    float currentInterval = validIntervals[validIntervals.Length - 1]*2;
+                    while (true)
+                    {
+                        float tickCount = length/ currentInterval;
+                        if (tickCount <= maxTickCount)
+                        {
+                            bestInterval = currentInterval;
+                            break;
+                        }
+
+                        currentInterval *= 2;
+                    }
+                }
+                else
+                    bestInterval = validIntervals[bestIntervalIdx];
+            }
+            else
+                bestInterval = validIntervals[bestIntervalIdx];
+
+            return bestInterval;
+        }
+
+        private void DrawTime(int xPos, float seconds, bool minutes)
+        {
+            TimeSpan timeSpan = TimeSpan.FromSeconds(seconds);
+
+            string timeString;
+            if (minutes)
+            {
+                timeString = timeSpan.TotalMinutes + ":" + timeSpan.Seconds.ToString("D2");
+            }
+            else
+            {
+                int hundredths = timeSpan.Milliseconds / 10;
+                timeString = timeSpan.TotalSeconds + "." + hundredths.ToString("D3");
+            }
+
+            Vector2I textBounds = GUIUtility.CalculateTextBounds(timeString, EditorBuiltin.DefaultFont,
+                EditorStyles.DefaultFontSize);
+
+            Vector2I textPosition = new Vector2I();
+            textPosition.x = xPos - textBounds.x / 2;
+            textPosition.y = TEXT_PADDING;
+
+            canvas.DrawText(timeString, textPosition, EditorBuiltin.DefaultFont, Color.LightGray,
+                EditorStyles.DefaultFontSize);
+        }
+
         private void Rebuild()
         private void Rebuild()
         {
         {
             canvas.Clear();
             canvas.Clear();
 
 
-            // TODO - Calculate interval sizes based on set range, width and FPS
-            //      - Dynamically change tick heights?
+            // TODO - Enforce a minimum limit between ticks, so when width is too small they don't come too close together
+            // TODO - Don't draw ticks below the frame rate
+
+            // TODO - Text from invisible ticks should be displayed (otherwise it will just pop in was the tick is shown)
+
+            // TODO - Draw small ticks (don't forget to handle offset properly)
+            // TODO - Transition between interval sizes more lightly (dynamically change tick height?)
 
 
+            // Constants
+            const int TEXT_SPACING = 10;
+            int maxTextWidth = GUIUtility.CalculateTextBounds("99:999", EditorBuiltin.DefaultFont, 
+                EditorStyles.DefaultFontSize).x;
+            int largeTickHeight = (int)(height * LARGE_TICK_HEIGHT_PCT);
             int drawableWidth = Math.Max(0, width - PADDING * 2);
             int drawableWidth = Math.Max(0, width - PADDING * 2);
             float rangeLength = rangeEnd - rangeStart;
             float rangeLength = rangeEnd - rangeStart;
-            float numSmallTicksPerLarge = 5.0f;
 
 
-            int totalNumFrames = MathEx.FloorToInt(rangeLength*fps);
-            int numVisibleTicks = Math.Min(totalNumFrames, MathEx.FloorToInt(drawableWidth / (float) MIN_TICK_DISTANCE));
+            // Draw ticks
+            float tickInterval = CalcTickInterval(rangeLength);
+            int numTicks = MathEx.FloorToInt(rangeLength / tickInterval) + 1;
 
 
-            float smallTickInterval = rangeLength / numVisibleTicks;
-            float largeTickInterval = smallTickInterval * numSmallTicksPerLarge;
+            bool displayAsMinutes = TimeSpan.FromSeconds(tickInterval).Minutes > 0;
 
 
-            float offsetLarge = MathEx.CeilToInt(rangeStart / largeTickInterval) * largeTickInterval - rangeStart;
-            float offsetSmall = MathEx.CeilToInt(rangeStart / smallTickInterval) * smallTickInterval - rangeStart;
+            float offset = rangeStart % tickInterval - rangeStart;
+            float t = offset;
+            int lastTextPosition = -1000;
+            for (int i = 0; i < numTicks; i++)
+            {
+                int xPos = (int)((t / rangeLength) * drawableWidth) + PADDING;
 
 
-            int largeTickHeight = (int)(height * LARGE_TICK_HEIGHT_PCT);
-            int smallTickHeight = (int)(height * SMALL_TICK_HEIGHT_PCT);
+                // Draw tick
+                Vector2I start = new Vector2I(xPos, height - largeTickHeight);
+                Vector2I end = new Vector2I(xPos, height);
 
 
-            bool drawSmallTicks = true; // TODO
+                canvas.DrawLine(start, end, Color.LightGray);
 
 
-            float t = offsetSmall;
-            for (int i = 0; i < numVisibleTicks; i++)
-            {
-                float distanceToLargeTick = MathEx.CeilToInt(t / largeTickInterval) * largeTickInterval - t;
-                if (MathEx.ApproxEquals(distanceToLargeTick, 0.0f))
+                // Draw text if it fits
+                int diff = xPos - lastTextPosition;
+                if (diff >= (maxTextWidth + TEXT_SPACING))
                 {
                 {
-                    int xPos = (int)((t/rangeLength)* drawableWidth) + PADDING;
+                    DrawTime(xPos, rangeStart + t, displayAsMinutes);
+                    lastTextPosition = xPos;
+                }
+                
+                // Move to next tick
+                t += tickInterval;
+                t = Math.Min(t, rangeLength);
+            }
 
 
-                    Vector2I start = new Vector2I(xPos, height - largeTickHeight);
-                    Vector2I end = new Vector2I(xPos, height);
+            //float offsetLarge = MathEx.CeilToInt(rangeStart / largeTickInterval) * largeTickInterval - rangeStart;
+            //float offsetSmall = MathEx.CeilToInt(rangeStart / smallTickInterval) * smallTickInterval - rangeStart;
 
 
-                    canvas.DrawLine(start, end, Color.LightGray);
+            //int largeTickHeight = (int)(height * LARGE_TICK_HEIGHT_PCT);
+            //int smallTickHeight = (int)(height * SMALL_TICK_HEIGHT_PCT);
 
 
-                    TimeSpan intervalSpan = TimeSpan.FromSeconds(largeTickInterval);
-                    TimeSpan timeSpan = TimeSpan.FromSeconds(rangeStart + t);
+            //bool drawSmallTicks = true; // TODO
 
 
-                    string timeString;
-                    if(intervalSpan.Minutes > 0)
-                        timeString = timeSpan.ToString(@"m\:ss");
-                    else
-                        timeString = timeSpan.ToString(@"ss\:fff");
+            //float t = offsetSmall;
+            //for (int i = 0; i < numVisibleTicks; i++)
+            //{
+            //    float distanceToLargeTick = MathEx.CeilToInt(t / largeTickInterval) * largeTickInterval - t;
+            //    if (MathEx.ApproxEquals(distanceToLargeTick, 0.0f))
+            //    {
+            //        int xPos = (int)((t/rangeLength)* drawableWidth) + PADDING;
 
 
-                    Vector2I textBounds = GUIUtility.CalculateTextBounds(timeString, EditorBuiltin.DefaultFont, 
-                        EditorStyles.DefaultFontSize);
+            //        Vector2I start = new Vector2I(xPos, height - largeTickHeight);
+            //        Vector2I end = new Vector2I(xPos, height);
 
 
-                    Vector2I textPosition = new Vector2I();
-                    textPosition.x = xPos - textBounds.x/2;
-                    textPosition.y = TEXT_PADDING;
+            //        canvas.DrawLine(start, end, Color.LightGray);
 
 
-                    canvas.DrawText(timeString, textPosition, EditorBuiltin.DefaultFont, Color.LightGray, 
-                        EditorStyles.DefaultFontSize);
-                }
-                else
-                {
-                    if (drawSmallTicks)
-                    {
-                        int xPos = (int)((t / rangeLength) * drawableWidth) + PADDING;
+            //        TimeSpan intervalSpan = TimeSpan.FromSeconds(largeTickInterval);
+            //        TimeSpan timeSpan = TimeSpan.FromSeconds(rangeStart + t);
 
 
-                        Vector2I start = new Vector2I(xPos, height - smallTickHeight);
-                        Vector2I end = new Vector2I(xPos, height);
+            //        string timeString;
+            //        if(intervalSpan.Minutes > 0)
+            //            timeString = timeSpan.ToString(@"m\:ss");
+            //        else
+            //            timeString = timeSpan.ToString(@"ss\:fff");
 
 
-                        canvas.DrawLine(start, end, Color.LightGray);
-                    }
-                }
+            //        Vector2I textBounds = GUIUtility.CalculateTextBounds(timeString, EditorBuiltin.DefaultFont, 
+            //            EditorStyles.DefaultFontSize);
 
 
-                t += smallTickInterval;
-            }
+            //        Vector2I textPosition = new Vector2I();
+            //        textPosition.x = xPos - textBounds.x/2;
+            //        textPosition.y = TEXT_PADDING;
+
+            //        canvas.DrawText(timeString, textPosition, EditorBuiltin.DefaultFont, Color.LightGray, 
+            //            EditorStyles.DefaultFontSize);
+            //    }
+            //    else
+            //    {
+            //        if (drawSmallTicks)
+            //        {
+            //            int xPos = (int)((t / rangeLength) * drawableWidth) + PADDING;
+
+            //            Vector2I start = new Vector2I(xPos, height - smallTickHeight);
+            //            Vector2I end = new Vector2I(xPos, height);
+
+            //            canvas.DrawLine(start, end, Color.LightGray);
+            //        }
+            //    }
+
+            //    t += smallTickInterval;
+            //}
         }
         }
     }
     }
 
 

+ 6 - 1
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -51,7 +51,12 @@ namespace BansheeEditor
             buttonLayout.AddElement(endField);
             buttonLayout.AddElement(endField);
             buttonLayout.AddElement(fpsField);
             buttonLayout.AddElement(fpsField);
 
 
-            timeline = new GUITimeline(GUI, 300, 20);
+            timeline = new GUITimeline(GUI, Width, 20);
+        }
+
+        protected override void WindowResized(int width, int height)
+        {
+            timeline.SetSize(width, 20);
         }
         }
 
 
         private void OnEditorUpdate()
         private void OnEditorUpdate()