Browse Source

Keyframes can now be added or removed in animation editor
Fixed canvas line drawing so the lines are drawn in the middle of the pixel, and applied same offset to triangles

BearishSun 9 years ago
parent
commit
3d275b8564

+ 9 - 7
Source/BansheeEngine/Source/BsGUICanvas.cpp

@@ -72,6 +72,8 @@ namespace BansheeEngine
 		for (auto& vertex : vertices)
 		{
 			Vector2 point = Vector2((float)vertex.x, (float)vertex.y);
+			point += Vector2(0.5f, 0.5f); // Offset to the middle of the pixel
+
 			mVertexData.push_back(point);
 		}
 
@@ -123,15 +125,15 @@ namespace BansheeEngine
 		{
 			if (i % 2 == 0)
 			{
-				mVertexData.push_back(Vector2((float)vertices[i - 2].x, (float)vertices[i - 2].y));
-				mVertexData.push_back(Vector2((float)vertices[i - 1].x, (float)vertices[i - 1].y));
-				mVertexData.push_back(Vector2((float)vertices[i - 0].x, (float)vertices[i - 0].y));
+				mVertexData.push_back(Vector2((float)vertices[i - 2].x + 0.5f, (float)vertices[i - 2].y + 0.5f));
+				mVertexData.push_back(Vector2((float)vertices[i - 1].x + 0.5f, (float)vertices[i - 1].y + 0.5f));
+				mVertexData.push_back(Vector2((float)vertices[i - 0].x + 0.5f, (float)vertices[i - 0].y + 0.5f));
 			}
 			else
 			{
-				mVertexData.push_back(Vector2((float)vertices[i - 0].x, (float)vertices[i - 0].y));
-				mVertexData.push_back(Vector2((float)vertices[i - 1].x, (float)vertices[i - 1].y));
-				mVertexData.push_back(Vector2((float)vertices[i - 2].x, (float)vertices[i - 2].y));
+				mVertexData.push_back(Vector2((float)vertices[i - 0].x + 0.5f, (float)vertices[i - 0].y + 0.5f));
+				mVertexData.push_back(Vector2((float)vertices[i - 1].x + 0.5f, (float)vertices[i - 1].y + 0.5f));
+				mVertexData.push_back(Vector2((float)vertices[i - 2].x + 0.5f, (float)vertices[i - 2].y + 0.5f));
 			}
 		}
 
@@ -165,7 +167,7 @@ namespace BansheeEngine
 		mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1));
 
 		for (auto& vertex : vertices)
-			mVertexData.push_back(Vector2((float)vertex.x, (float)vertex.y));
+			mVertexData.push_back(Vector2((float)vertex.x + 0.5f, (float)vertex.y + 0.5f));
 
 		mTriangleElementData.push_back(TriangleElementData());
 		TriangleElementData& elemData = mTriangleElementData.back();

+ 16 - 8
Source/MBansheeEditor/Windows/Animation/GUICurveDrawing.cs

@@ -28,7 +28,7 @@ namespace BansheeEditor
         private float xRange = 60.0f;
         private float yRange = 20.0f;
         private int fps = 1;
-        private int markedFrameIdx = -1;
+        private int markedFrameIdx = 0;
 
         private int drawableWidth;
         private GUICanvas canvas;
@@ -145,6 +145,20 @@ namespace BansheeEditor
             }
         }
 
+        /// <summary>
+        /// Returns time for a frame with the specified index. Depends on set range and FPS.
+        /// </summary>
+        /// <param name="frameIdx">Index of the frame (not a key-frame) to get the time for.</param>
+        /// <returns>Time of the frame with the provided index. </returns>
+        public float GetTimeForFrame(int frameIdx)
+        {
+            float range = GetRange();
+            int numFrames = (int)range * fps;
+            float timePerFrame = range / numFrames;
+
+            return frameIdx* timePerFrame;
+        }
+
         /// <summary>
         /// Retrieves information under the provided window coordinates. This involves coordinates of the curve, as well
         /// as curve and key-frame indices that were under the coordinates (if any).
@@ -378,13 +392,7 @@ namespace BansheeEditor
 
             // Draw selected frame marker
             if (markedFrameIdx != -1)
-            {
-                float range = GetRange();
-                int numFrames = (int)range * fps;
-                float timePerFrame = range / numFrames;
-
-                DrawFrameMarker(markedFrameIdx*timePerFrame, Color.BansheeOrange);
-            }
+                DrawFrameMarker(GetTimeForFrame(markedFrameIdx), Color.BansheeOrange);
         }
 
         /// <summary>

+ 118 - 9
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -1,5 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+
+using System.Collections.Generic;
 using BansheeEngine;
 
 namespace BansheeEditor
@@ -13,6 +15,18 @@ namespace BansheeEditor
     /// </summary>
     internal class AnimationWindow : EditorWindow
     {
+        public struct KeyframeRef
+        {
+            public KeyframeRef(int curveIdx, int keyIdx)
+            {
+                this.curveIdx = curveIdx;
+                this.keyIdx = keyIdx;
+            }
+
+            public int curveIdx;
+            public int keyIdx;
+        }
+
         private GUIGraphTime timeline;
         private GUICurveDrawing curveDrawing;
         private GUIGraphValues sidebar;
@@ -20,9 +34,19 @@ namespace BansheeEditor
         private GUIFloatField lengthField;
         private GUIIntField fpsField;
         private GUIFloatField yRangeField;
+        private GUIButton addKeyframeBtn;
 
         private GUILayout buttonLayout;
 
+        private EdAnimationCurve[] curves = new EdAnimationCurve[0];
+        private int markedFrameIdx;
+        private List<KeyframeRef> selectedKeyframes = new List<KeyframeRef>();
+
+        // Keyframe drag
+        private bool isMousePressedOverKey;
+        private KeyFrame[] draggedKeyframes;
+        private Vector2 dragStart;
+
         /// <summary>
         /// Opens the animation window.
         /// </summary>
@@ -43,6 +67,7 @@ namespace BansheeEditor
             lengthField = new GUIFloatField(new LocEdString("Length"), 50);
             fpsField = new GUIIntField(new LocEdString("FPS"), 50);
             yRangeField = new GUIFloatField(new LocEdString("Y range"), 50);
+            addKeyframeBtn = new GUIButton(new LocEdString("Add keyframe"));
 
             lengthField.Value = 60.0f;
             fpsField.Value = 1;
@@ -63,6 +88,10 @@ namespace BansheeEditor
                 curveDrawing.SetRange(lengthField.Value, x);
                 sidebar.SetRange(x * -0.5f, x * 0.5f);
             };
+            addKeyframeBtn.OnClick += () =>
+            {
+                AddKeyframeAtMarker();
+            };
 
             GUILayout mainLayout = GUI.AddLayoutY();
 
@@ -74,10 +103,12 @@ namespace BansheeEditor
             buttonLayout.AddSpace(5);
             buttonLayout.AddElement(fpsField);
             buttonLayout.AddSpace(5);
+            buttonLayout.AddElement(addKeyframeBtn);
+            buttonLayout.AddSpace(5);
 
             timeline = new GUIGraphTime(mainLayout, Width, 20);
 
-            EdAnimationCurve[] curves = CreateDummyCurves();
+            curves = CreateDummyCurves();
             curveDrawing = new GUICurveDrawing(mainLayout, Width, Height - 20, curves);
             curveDrawing.SetRange(60.0f, 20.0f);
 
@@ -90,12 +121,46 @@ namespace BansheeEditor
             curveDrawing.SetSize(Width, Height - 20 - buttonLayout.Bounds.height);
             curveDrawing.Rebuild();
 
-            Debug.Log("CURVE DRAWING HEIGHT + " + (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
         }
 
+        private void AddKeyframeAtMarker()
+        {
+            ClearSelection();
+
+            foreach (var curve in curves)
+            {
+                float t = curveDrawing.GetTimeForFrame(markedFrameIdx);
+                float value = curve.Native.Evaluate(t);
+
+                curve.AddKeyframe(t, value);
+            }
+
+            curveDrawing.Rebuild();
+        }
+
+        private void DeleteSelectedKeyframes()
+        {
+            // Sort keys from highest to lowest so they can be removed without changing the indices of the keys
+            // after them
+            selectedKeyframes.Sort((x, y) =>
+            {
+                if (x.curveIdx.Equals(y.curveIdx))
+                    return y.keyIdx.CompareTo(x.keyIdx);
+
+                return x.curveIdx.CompareTo(y.curveIdx);
+            });
+
+            foreach (var keyframe in selectedKeyframes)
+                curves[keyframe.curveIdx].RemoveKeyframe(keyframe.keyIdx);
+
+            ClearSelection();
+
+            curveDrawing.Rebuild();
+        }
+
         private EdAnimationCurve[] CreateDummyCurves()
         {
             EdAnimationCurve[] curves = new EdAnimationCurve[1];
@@ -118,9 +183,16 @@ namespace BansheeEditor
             curveDrawing.Rebuild();
         }
 
+        private void ClearSelection()
+        {
+            curveDrawing.ClearSelectedKeyframes();
+            selectedKeyframes.Clear();
+            isMousePressedOverKey = false;
+        }
+
         private void OnEditorUpdate()
         {
-            if (Input.IsPointerButtonHeld(PointerButton.Left))
+            if (Input.IsPointerButtonDown(PointerButton.Left))
             {
                 Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
 
@@ -129,24 +201,61 @@ namespace BansheeEditor
                 int keyIdx;
                 if (curveDrawing.GetCoordInfo(windowPos, out curveCoord, out curveIdx, out keyIdx))
                 {
-                    if(keyIdx == -1)
-                        curveDrawing.ClearSelectedKeyframes();
+                    if (keyIdx == -1)
+                        ClearSelection();
                     else
+                    {
+                        if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
+                            ClearSelection();
+
                         curveDrawing.SelectKeyframe(curveIdx, keyIdx, true);
 
+                        int existingIdx = selectedKeyframes.FindIndex(x =>
+                        {
+                            return x.curveIdx == curveIdx && x.keyIdx == keyIdx;
+                        });
+
+                        if (existingIdx == -1)
+                            selectedKeyframes.Add(new KeyframeRef(curveIdx, keyIdx));
+
+                        isMousePressedOverKey = true;
+                        dragStart = curveCoord;
+                    }
+
                     curveDrawing.Rebuild();
+                }
+            }
+            else if (Input.IsPointerButtonHeld(PointerButton.Left))
+            {
+                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
 
-                    Debug.Log("Click coord: " + curveCoord + " - " + curveIdx + " - " + keyIdx);
+                if (isMousePressedOverKey)
+                {
+                    // TODO - Check if pointer moves some minimal amount
+                    // - If so, start drag. Record all current positions
+                    // - Calculate offset in curve space and apply to all keyframes
                 }
                 else
                 {
                     int frameIdx = timeline.GetFrame(windowPos);
-                    timeline.SetMarkedFrame(frameIdx);
-                    curveDrawing.SetMarkedFrame(frameIdx);
 
-                    curveDrawing.Rebuild();
+                    if (frameIdx != -1)
+                    {
+                        timeline.SetMarkedFrame(frameIdx);
+                        curveDrawing.SetMarkedFrame(frameIdx);
+
+                        markedFrameIdx = frameIdx;
+                        curveDrawing.Rebuild();
+                    }
                 }
             }
+            else if (Input.IsPointerButtonUp(PointerButton.Left))
+            {
+                isMousePressedOverKey = false;
+            }
+
+            if(Input.IsButtonUp(ButtonCode.Delete))
+                DeleteSelectedKeyframes();
         }
     }