Browse Source

Added GUI side value drawing for animation editor

BearishSun 9 years ago
parent
commit
84c91af670

+ 2 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -51,6 +51,8 @@
     <Compile Include="Windows\AnimationWindow.cs" />
     <Compile Include="Windows\Animation\GUICurveDrawing.cs" />
     <Compile Include="Windows\Animation\GUIFieldSelector.cs" />
+    <Compile Include="Windows\Animation\GUIGraphValues.cs" />
+    <Compile Include="Windows\Animation\GUITicks.cs" />
     <Compile Include="Windows\Animation\GUITimeline.cs" />
     <Compile Include="Windows\BrowseDialog.cs" />
     <Compile Include="Windows\Build\BuildManager.cs" />

+ 136 - 0
Source/MBansheeEditor/Windows/Animation/GUIGraphValues.cs

@@ -0,0 +1,136 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup AnimationEditor
+     *  @{
+     */
+
+    internal class GUIGraphValues
+    {
+        private static readonly Color COLOR_DARK_GRAY = new Color(40.0f / 255.0f, 40.0f / 255.0f, 40.0f / 255.0f, 1.0f);
+
+        private GUITicks tickHandler;
+        private GUICanvas canvas;
+
+        private int width = 20;
+        private int height = 20;
+        private float rangeStart = -1.0f;
+        private float rangeEnd = 1.0f;
+
+        private float maxTextHeight;
+
+        public GUIGraphValues(GUILayout layout, int width, int height)
+        {
+            canvas = new GUICanvas();
+            layout.AddElement(canvas);
+
+            tickHandler = new GUITicks();
+
+            maxTextHeight = GUIUtility.CalculateTextBounds("99:999", EditorBuiltin.DefaultFont,
+               EditorStyles.DefaultFontSize).y;
+
+            SetSize(width, height);
+        }
+
+        public void SetSize(int width, int height)
+        {
+            this.width = width;
+            this.height = height;
+
+            canvas.SetWidth(width);
+            canvas.SetHeight(height);
+
+            tickHandler.SetRange(rangeStart, rangeEnd, height);
+
+            Rebuild();
+        }
+
+        public void SetRange(float start, float end)
+        {
+            if (start > end)
+            {
+                float temp = start;
+                start = end;
+                end = temp;
+            }
+
+            rangeStart = start;
+            rangeEnd = end;
+
+            tickHandler.SetRange(rangeStart, rangeEnd, height);
+
+            Rebuild();
+        }
+
+        private void DrawTime(int yPos, float seconds, bool minutes)
+        {
+            TimeSpan timeSpan = TimeSpan.FromSeconds(seconds);
+
+            string timeString;
+            if (minutes)
+                timeString = timeSpan.TotalMinutes.ToString("#0") + ":" + timeSpan.Seconds.ToString("D2");
+            else
+                timeString = timeSpan.TotalSeconds.ToString("#0.00");
+
+            Vector2I textBounds = GUIUtility.CalculateTextBounds(timeString, EditorBuiltin.DefaultFont,
+                EditorStyles.DefaultFontSize);
+
+            Vector2I textPosition = new Vector2I();
+            textPosition.x = width - textBounds.x;
+            textPosition.y = yPos - textBounds.y;
+
+            canvas.DrawText(timeString, textPosition, EditorBuiltin.DefaultFont, Color.LightGray,
+                EditorStyles.DefaultFontSize);
+        }
+
+        private void Rebuild()
+        {
+            canvas.Clear();
+
+            int heightOffset = height/2;
+            float pixelsPerHeight;
+
+            if (rangeEnd != rangeStart)
+                pixelsPerHeight = height/(rangeEnd - rangeStart);
+            else
+                pixelsPerHeight = 0;
+
+            int numTickLevels = tickHandler.NumLevels;
+            for (int i = numTickLevels - 1; i >= 0; i--)
+            {
+                float[] ticks = tickHandler.GetTicks(i);
+                float strength = tickHandler.GetLevelStrength(i);
+               
+                if (ticks.Length > 0)
+                {
+                    float valuePerTick = (rangeEnd - rangeStart)/ticks.Length;
+                    bool displayAsMinutes = TimeSpan.FromSeconds(valuePerTick).Minutes > 0;
+
+                    for (int j = 0; j < ticks.Length; j++)
+                    {
+                        int yPos = (int) (ticks[j]*pixelsPerHeight);
+                        yPos = heightOffset - yPos; // Offset and flip height (canvas Y goes down)
+
+                        Vector2I start = new Vector2I(0, yPos);
+                        Vector2I end = new Vector2I((int) (width*strength), yPos);
+
+                        Color color = Color.LightGray;
+                        color.a *= strength;
+
+                        canvas.DrawLine(start, end, color);
+
+                        // Draw text for the highest level ticks
+                        if (i == 0)
+                            DrawTime(yPos, ticks[j], displayAsMinutes);
+                    }
+                }
+            }
+        }
+    }
+
+    /** @} */
+}

+ 142 - 0
Source/MBansheeEditor/Windows/Animation/GUITicks.cs

@@ -0,0 +1,142 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup AnimationEditor
+     *  @{
+     */
+
+    internal enum GUITickStepType
+    {
+        Time,
+        Generic
+    }
+
+    // TODO DOC
+    internal class GUITicks
+    {
+        private int pixelRange = 100;
+        private float valueRangeStart = 0.0f;
+        private float valueRangeEnd = 1.0f;
+
+        private int minTickSpacingPx = 5;
+        private int maxTickSpacingPx = 30;
+
+        private float[] validSteps = new [] { 1.0f };
+        private float[] levelStrengths = new[] { 1.0f };
+        private int numLevels = 1;
+        private int maxLevel = 0;
+
+        public int NumLevels { get { return numLevels; } }
+
+        internal GUITicks(GUITickStepType stepType = GUITickStepType.Generic)
+        {
+            if(stepType == GUITickStepType.Generic)
+                SetGenericSteps();
+            else
+                SetTimeSteps();
+
+            Rebuild();
+        }
+
+        internal void SetRange(float valueRangeStart, float valueRangeEnd, int pixelRange)
+        {
+            this.valueRangeStart = valueRangeStart;
+            this.valueRangeEnd = valueRangeEnd;
+            this.pixelRange = pixelRange;
+
+            Rebuild();
+        }
+
+        internal void SetTickSpacing(int minPx, int maxPx)
+        {
+            minTickSpacingPx = minPx;
+            maxTickSpacingPx = maxPx;
+
+            Rebuild();
+        }
+
+        internal float GetLevelStrength(int level)
+        {
+            if (level < 0 || level >= numLevels)
+                return 0.0f;
+
+            return levelStrengths[maxLevel + level];
+        }
+
+        internal float[] GetTicks(int level)
+        {
+            if (level < 0 || level >= numLevels)
+                return new float[0];
+
+            float step = validSteps[maxLevel + level];
+
+            // Round up and down so we get one extra tick on either side (outside of value range)
+            int startTick = MathEx.FloorToInt(valueRangeStart / step);
+            int endTick = MathEx.CeilToInt(valueRangeEnd / step);
+
+            int numTicks = endTick - startTick + 1;
+
+            float[] ticks = new float[numTicks];
+            for (int i = startTick; i <= endTick; i++)
+                ticks[i - startTick] = i*step;
+
+            return ticks;
+        }
+
+        private void Rebuild()
+        {
+            levelStrengths = new float[validSteps.Length];
+            maxLevel = 0;
+
+            float valueRange = valueRangeEnd - valueRangeStart;
+            int tickSpacing = maxTickSpacingPx - minTickSpacingPx;
+            int i = 0;
+            for (; i < validSteps.Length; i++)
+            {
+                float tickSpacingPx = (validSteps[i]/valueRange) * pixelRange;
+                levelStrengths[i] = (tickSpacingPx - minTickSpacingPx)/tickSpacing;
+
+                if (levelStrengths[i] > 1.0f)
+                    maxLevel = i;
+                else if (levelStrengths[i] < 0.0f)
+                    break;
+            }
+
+            if (i > 0)
+                numLevels = i - maxLevel;
+            else
+                numLevels = 0;
+        }
+
+        private void SetTimeSteps()
+        {
+            validSteps = new float[]
+            {
+                3600.0f, 1800.0f, 600.0f, 300.0f,
+                60.0f, 30.0f, 10.0f, 5.0f,
+                1.0f, 0.5f, 0.25f, 0.1f, 0.05f, 0.01f
+            };
+        }
+
+        private void SetGenericSteps()
+        {
+            float minStep = 0.0000001f;
+            int numSteps = 15;
+
+            validSteps = new float[15 * 2];
+            for (int i = numSteps - 1; i >= 0; i--)
+            {
+                validSteps[i * 2 + 1] = minStep;
+                validSteps[i * 2 + 0] = minStep * 5;
+
+                minStep *= 10.0f;
+            }
+        }
+    }
+
+    /** @} */
+}

+ 13 - 2
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -15,10 +15,14 @@ namespace BansheeEditor
     {
         private GUITimeline timeline;
         private GUICurveDrawing curveDrawing;
+        private GUIGraphValues sidebar;
+
         private GUIFloatField lengthField;
         private GUIIntField fpsField;
         private GUIFloatField yRangeField;
 
+        private GUILayout buttonLayout;
+
         /// <summary>
         /// Opens the animation window.
         /// </summary>
@@ -57,11 +61,12 @@ namespace BansheeEditor
             yRangeField.OnChanged += x =>
             {
                 curveDrawing.SetRange(lengthField.Value, x);
+                sidebar.SetRange(x * -0.5f, x * 0.5f);
             };
 
             GUILayout mainLayout = GUI.AddLayoutY();
 
-            GUILayout buttonLayout = mainLayout.AddLayoutX();
+            buttonLayout = mainLayout.AddLayoutX();
             buttonLayout.AddSpace(5);
             buttonLayout.AddElement(lengthField);
             buttonLayout.AddSpace(5);
@@ -76,6 +81,11 @@ namespace BansheeEditor
             curveDrawing = new GUICurveDrawing(mainLayout, Width, Height - 20, curves);
             curveDrawing.SetRange(60.0f, 20.0f);
 
+            GUIPanel sidebarPanel = GUI.AddPanel(-10);
+            sidebarPanel.SetPosition(0, 20 + buttonLayout.Bounds.height);
+
+            sidebar = new GUIGraphValues(sidebarPanel, 40, Height - 20 - buttonLayout.Bounds.height);
+
             // TODO - Calculate min/max Y and range to set as default
             //  - Also recalculate whenever curves change and increase as needed
         }
@@ -96,7 +106,8 @@ namespace BansheeEditor
         protected override void WindowResized(int width, int height)
         {
             timeline.SetSize(width, 20);
-            curveDrawing.SetSize(width, height - 20);
+            curveDrawing.SetSize(width, height - 20 - buttonLayout.Bounds.height);
+            sidebar.SetSize(40, height - 20 - buttonLayout.Bounds.height);
         }
 
         private void OnEditorUpdate()