Переглянути джерело

Added main animation editor window buttons
Animation editor now updates field display values as frames change

BearishSun 9 роки тому
батько
коміт
33dadc096f

+ 2 - 2
Source/MBansheeEditor/Windows/Animation/GUIAnimFieldDisplay.cs

@@ -110,7 +110,7 @@ namespace BansheeEditor
             }
             }
         }
         }
 
 
-        private SerializableProperty FindProperty(string path)
+        public static SerializableProperty FindProperty(SceneObject root, string path)
         {
         {
             if (string.IsNullOrEmpty(path) || root == null)
             if (string.IsNullOrEmpty(path) || root == null)
                 return null;
                 return null;
@@ -258,7 +258,7 @@ namespace BansheeEditor
             fields = new GUIAnimFieldEntry[paths.Count];
             fields = new GUIAnimFieldEntry[paths.Count];
             for (int i = 0; i < paths.Count; i++)
             for (int i = 0; i < paths.Count; i++)
             {
             {
-                SerializableProperty property = FindProperty(paths[i]);
+                SerializableProperty property = FindProperty(root, paths[i]);
                 if (property != null)
                 if (property != null)
                 {
                 {
                     switch (property.Type)
                     switch (property.Type)

+ 19 - 8
Source/MBansheeEditor/Windows/Animation/GUICurveEditor.cs

@@ -66,6 +66,11 @@ namespace BansheeEditor
         private TangentRef draggedTangent;
         private TangentRef draggedTangent;
         private Vector2I dragStart;
         private Vector2I dragStart;
 
 
+        /// <summary>
+        /// Triggers whenever user selects a new frame.
+        /// </summary>
+        public Action<int> OnFrameSelected;
+
         /// <summary>
         /// <summary>
         /// Returns the displayed range of the curve on the x axis (time).
         /// Returns the displayed range of the curve on the x axis (time).
         /// </summary>
         /// </summary>
@@ -89,7 +94,6 @@ namespace BansheeEditor
 
 
             blankContextMenu = new ContextMenu();
             blankContextMenu = new ContextMenu();
             blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);
             blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);
-            blankContextMenu.AddItem("Add event", AddEventAtPosition);
 
 
             keyframeContextMenu = new ContextMenu();
             keyframeContextMenu = new ContextMenu();
             keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
             keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
@@ -172,6 +176,8 @@ namespace BansheeEditor
 
 
                     if (frameIdx != -1)
                     if (frameIdx != -1)
                         SetMarkedFrame(frameIdx);
                         SetMarkedFrame(frameIdx);
+
+                    OnFrameSelected?.Invoke(frameIdx);
                 }
                 }
 
 
                 isPointerHeld = true;
                 isPointerHeld = true;
@@ -343,6 +349,8 @@ namespace BansheeEditor
 
 
                     if (frameIdx != -1)
                     if (frameIdx != -1)
                         SetMarkedFrame(frameIdx);
                         SetMarkedFrame(frameIdx);
+
+                    OnFrameSelected?.Invoke(frameIdx);
                 }
                 }
             }
             }
         }
         }
@@ -370,8 +378,6 @@ namespace BansheeEditor
             this.curves = curves;
             this.curves = curves;
             guiCurveDrawing.SetCurves(curves);
             guiCurveDrawing.SetCurves(curves);
 
 
-            // TODO - Recalculate valid size
-
             Redraw();
             Redraw();
         }
         }
 
 
@@ -419,6 +425,16 @@ namespace BansheeEditor
             Redraw();
             Redraw();
         }
         }
 
 
+        /// <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)
+        {
+            return guiCurveDrawing.GetTimeForFrame(markedFrameIdx);
+        }
+
         /// <summary>
         /// <summary>
         /// Sets the frame at which to display the frame marker.
         /// Sets the frame at which to display the frame marker.
         /// </summary>
         /// </summary>
@@ -525,11 +541,6 @@ namespace BansheeEditor
             }
             }
         }
         }
 
 
-        private void AddEventAtPosition()
-        {
-            // TODO
-        }
-
         private void DeleteSelectedKeyframes()
         private void DeleteSelectedKeyframes()
         {
         {
             foreach (var selectedEntry in selectedKeyframes)
             foreach (var selectedEntry in selectedKeyframes)

+ 329 - 48
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -16,13 +16,29 @@ namespace BansheeEditor
     [DefaultSize(900, 500)]
     [DefaultSize(900, 500)]
     internal class AnimationWindow : EditorWindow
     internal class AnimationWindow : EditorWindow
     {
     {
+        /// <summary>
+        /// A set of animation curves for a field of a certain type.
+        /// </summary>
+        private struct FieldCurves
+        {
+            public SerializableProperty.FieldType type;
+            public EdAnimationCurve[] curves;
+        }
+
         private const int FIELD_DISPLAY_WIDTH = 200;
         private const int FIELD_DISPLAY_WIDTH = 200;
 
 
         private bool isInitialized;
         private bool isInitialized;
-        private GUIFloatField lengthField;
-        private GUIIntField fpsField;
-        private GUIFloatField yRangeField;
-        private GUIButton addKeyframeBtn;
+        private GUIButton playButton;
+        private GUIButton recordButton;
+
+        private GUIButton prevFrameButton;
+        private GUIIntField frameInputField;
+        private GUIButton nextFrameButton;
+
+        private GUIButton addKeyframeButton;
+        private GUIButton addEventButton;
+
+        private GUIButton optionsButton;
 
 
         private GUIButton addPropertyBtn;
         private GUIButton addPropertyBtn;
         private GUIButton delPropertyBtn;
         private GUIButton delPropertyBtn;
@@ -34,9 +50,19 @@ namespace BansheeEditor
         private GUIAnimFieldDisplay guiFieldDisplay;
         private GUIAnimFieldDisplay guiFieldDisplay;
         private GUICurveEditor guiCurveEditor;
         private GUICurveEditor guiCurveEditor;
 
 
-        private Dictionary<string, EdAnimationCurve> curves = new Dictionary<string, EdAnimationCurve>();
+        private SceneObject selectedSO;
+        private int currentFrameIdx;
+        private int fps = 1;
+
+        private Dictionary<string, FieldCurves> curves = new Dictionary<string, FieldCurves>();
         private List<string> selectedFields = new List<string>();
         private List<string> selectedFields = new List<string>();
 
 
+        internal int FPS
+        {
+            get { return fps; }
+            set { guiCurveEditor.SetFPS(value); fps = MathEx.Max(value, 1); }
+        }
+
         /// <summary>
         /// <summary>
         /// Opens the animation window.
         /// Opens the animation window.
         /// </summary>
         /// </summary>
@@ -96,7 +122,7 @@ namespace BansheeEditor
             curves.Clear();
             curves.Clear();
             isInitialized = false;
             isInitialized = false;
 
 
-            SceneObject selectedSO = Selection.SceneObject;
+            selectedSO = Selection.SceneObject;
             if (selectedSO == null)
             if (selectedSO == null)
             {
             {
                 GUILabel warningLbl = new GUILabel(new LocEdString("Select an object to animate in the Hierarchy or Scene windows."));
                 GUILabel warningLbl = new GUILabel(new LocEdString("Select an object to animate in the Hierarchy or Scene windows."));
@@ -116,18 +142,86 @@ namespace BansheeEditor
             // TODO - Retrieve Animation & AnimationClip from the selected object, fill curves dictionary
             // TODO - Retrieve Animation & AnimationClip from the selected object, fill curves dictionary
             //  - If not available, show a button to create new animation clip
             //  - If not available, show a button to create new animation clip
 
 
-            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"));
+            // Top button row
+            GUIContent playIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.Play),
+                new LocEdString("Play"));
+            GUIContent recordIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.Record),
+                new LocEdString("Record"));
+
+            GUIContent prevFrameIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.FrameBack),
+                new LocEdString("Previous frame"));
+            GUIContent nextFrameIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.FrameForward),
+                new LocEdString("Next frame"));
+
+            GUIContent addKeyframeIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.AddKeyframe),
+                new LocEdString("Add keyframe"));
+            GUIContent addEventIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.AddEvent),
+                new LocEdString("Add event"));
+
+            GUIContent optionsIcon = new GUIContent(EditorBuiltin.GetLibraryWindowIcon(LibraryWindowIcon.Options),
+                new LocEdString("Options"));
+
+            playButton = new GUIButton(playIcon);
+            recordButton = new GUIButton(recordIcon);
+
+            prevFrameButton = new GUIButton(prevFrameIcon);
+            frameInputField = new GUIIntField();
+            nextFrameButton = new GUIButton(nextFrameIcon);
+
+            addKeyframeButton = new GUIButton(addKeyframeIcon);
+            addEventButton = new GUIButton(addEventIcon);
+
+            optionsButton = new GUIButton(optionsIcon);
+
+            playButton.OnClick += () =>
+            {
+                // TODO
+                // - Record current state of the scene object hierarchy
+                // - Evaluate all curves manually and update them
+                // - On end, restore original values of the scene object hierarchy
+            };
+
+            recordButton.OnClick += () =>
+            {
+                // TODO
+                // - Every frame read back current values of all the current curve's properties and assign it to the current frame
+            };
+
+            prevFrameButton.OnClick += () =>
+            {
+                SetCurrentFrame(currentFrameIdx - 1);
+            };
+
+            frameInputField.OnChanged += SetCurrentFrame;
+
+            nextFrameButton.OnClick += () =>
+            {
+                SetCurrentFrame(currentFrameIdx + 1);
+            };
+
+            addKeyframeButton.OnClick += () =>
+            {
+                guiCurveEditor.AddKeyFrameAtMarker();
+
+                // TODO - Update local curves?
+            };
+
+            addEventButton.OnClick += () =>
+            {
+                // TODO - Add event
+            };
+
+            optionsButton.OnClick += () =>
+            {
+                Vector2I openPosition = ScreenToWindowPos(Input.PointerPosition);
+                AnimationOptions dropDown = DropDownWindow.Open<AnimationOptions>(this, openPosition);
+                dropDown.Initialize(this);
+            };
 
 
+            // Property buttons
             addPropertyBtn = new GUIButton(new LocEdString("Add property"));
             addPropertyBtn = new GUIButton(new LocEdString("Add property"));
             delPropertyBtn = new GUIButton(new LocEdString("Delete selected"));
             delPropertyBtn = new GUIButton(new LocEdString("Delete selected"));
 
 
-            lengthField.Value = 60.0f;
-            fpsField.Value = 1;
-            yRangeField.Value = 20.0f;
-
             addPropertyBtn.OnClick += () =>
             addPropertyBtn.OnClick += () =>
             {
             {
                 Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
                 Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
@@ -149,37 +243,24 @@ namespace BansheeEditor
                 });
                 });
             };
             };
 
 
-            lengthField.OnChanged += x =>
-            {
-                guiCurveEditor.SetRange(lengthField.Value, yRangeField.Value);
-            };
-            fpsField.OnChanged += x =>
-            {
-                guiCurveEditor.SetFPS(x);
-            };
-            yRangeField.OnChanged += x =>
-            {
-                guiCurveEditor.SetRange(lengthField.Value, yRangeField.Value);
-            };
-            addKeyframeBtn.OnClick += () =>
-            {
-                guiCurveEditor.AddKeyFrameAtMarker();
-            };
-
             GUILayout mainLayout = GUI.AddLayoutY();
             GUILayout mainLayout = GUI.AddLayoutY();
 
 
             buttonLayout = mainLayout.AddLayoutX();
             buttonLayout = mainLayout.AddLayoutX();
             buttonLayout.AddSpace(5);
             buttonLayout.AddSpace(5);
-            buttonLayout.AddElement(lengthField);
+            buttonLayout.AddElement(playButton);
+            buttonLayout.AddElement(recordButton);
             buttonLayout.AddSpace(5);
             buttonLayout.AddSpace(5);
-            buttonLayout.AddElement(yRangeField);
+            buttonLayout.AddElement(prevFrameButton);
+            buttonLayout.AddElement(frameInputField);
+            buttonLayout.AddElement(nextFrameButton);
             buttonLayout.AddSpace(5);
             buttonLayout.AddSpace(5);
-            buttonLayout.AddElement(fpsField);
-            buttonLayout.AddSpace(5);
-            buttonLayout.AddElement(addKeyframeBtn);
+            buttonLayout.AddElement(addKeyframeButton);
+            buttonLayout.AddElement(addEventButton);
             buttonLayout.AddSpace(5);
             buttonLayout.AddSpace(5);
+            buttonLayout.AddElement(optionsButton);
+            buttonLayout.AddFlexibleSpace();
 
 
-            buttonLayoutHeight = lengthField.Bounds.height;
+            buttonLayoutHeight = playButton.Bounds.height;
 
 
             GUILayout contentLayout = mainLayout.AddLayoutX();
             GUILayout contentLayout = mainLayout.AddLayoutX();
             GUILayout fieldDisplayLayout = contentLayout.AddLayoutY(GUIOption.FixedWidth(FIELD_DISPLAY_WIDTH));
             GUILayout fieldDisplayLayout = contentLayout.AddLayoutY(GUIOption.FixedWidth(FIELD_DISPLAY_WIDTH));
@@ -198,11 +279,87 @@ namespace BansheeEditor
 
 
             int curveEditorWidth = Math.Max(0, Width - FIELD_DISPLAY_WIDTH);
             int curveEditorWidth = Math.Max(0, Width - FIELD_DISPLAY_WIDTH);
             guiCurveEditor = new GUICurveEditor(this, editorPanel, curveEditorWidth, Height - buttonLayoutHeight);
             guiCurveEditor = new GUICurveEditor(this, editorPanel, curveEditorWidth, Height - buttonLayoutHeight);
+            guiCurveEditor.OnFrameSelected += OnFrameSelected;
             guiCurveEditor.Redraw();
             guiCurveEditor.Redraw();
 
 
+            SetCurrentFrame(currentFrameIdx);
             isInitialized = true;
             isInitialized = true;
         }
         }
 
 
+        private void SetCurrentFrame(int frameIdx)
+        {
+            currentFrameIdx = Math.Max(0, frameIdx);
+
+            frameInputField.Value = currentFrameIdx;
+            guiCurveEditor.SetMarkedFrame(currentFrameIdx);
+
+            float time = guiCurveEditor.GetTimeForFrame(currentFrameIdx);
+
+            List<GUIAnimFieldPathValue> values = new List<GUIAnimFieldPathValue>();
+            foreach (var kvp in curves)
+            {
+                SerializableProperty property = GUIAnimFieldDisplay.FindProperty(selectedSO, kvp.Key);
+                if (property != null)
+                {
+                    GUIAnimFieldPathValue fieldValue = new GUIAnimFieldPathValue();
+                    fieldValue.path = kvp.Key;
+
+                    switch (kvp.Value.type)
+                    {
+                        case SerializableProperty.FieldType.Vector2:
+                        {
+                            Vector2 value = new Vector2();
+
+                            for(int i = 0; i < 2; i++)
+                                value[i] = kvp.Value.curves[i].Evaluate(time, false);
+
+                            fieldValue.value = value;
+                        }
+                            break;
+                        case SerializableProperty.FieldType.Vector3:
+                            {
+                                Vector3 value = new Vector3();
+
+                                for (int i = 0; i < 3; i++)
+                                    value[i] = kvp.Value.curves[i].Evaluate(time, false);
+
+                                fieldValue.value = value;
+                            }
+                            break;
+                        case SerializableProperty.FieldType.Vector4:
+                            {
+                                Vector4 value = new Vector4();
+
+                                for (int i = 0; i < 4; i++)
+                                    value[i] = kvp.Value.curves[i].Evaluate(time, false);
+
+                                fieldValue.value = value;
+                            }
+                            break;
+                        case SerializableProperty.FieldType.Color:
+                            {
+                                Color value = new Color();
+
+                                for (int i = 0; i < 4; i++)
+                                    value[i] = kvp.Value.curves[i].Evaluate(time, false);
+
+                                fieldValue.value = value;
+                            }
+                            break;
+                        case SerializableProperty.FieldType.Bool:
+                        case SerializableProperty.FieldType.Int:
+                        case SerializableProperty.FieldType.Float:
+                            fieldValue.value = kvp.Value.curves[0].Evaluate(time, false); ;
+                            break;
+                    }
+
+                    values.Add(fieldValue);
+                }
+            }
+
+            guiFieldDisplay.SetDisplayValues(values.ToArray());
+        }
+
         private void OnPointerPressed(PointerEvent ev)
         private void OnPointerPressed(PointerEvent ev)
         {
         {
             if (!isInitialized)
             if (!isInitialized)
@@ -241,7 +398,7 @@ namespace BansheeEditor
             for (int i = 0; i < selectedFields.Count; i++)
             for (int i = 0; i < selectedFields.Count; i++)
             {
             {
                 EdAnimationCurve curve;
                 EdAnimationCurve curve;
-                if(curves.TryGetValue(selectedFields[i], out curve))
+                if(TryGetCurve(selectedFields[i], out curve))
                     curvesToDisplay.Add(curve);
                     curvesToDisplay.Add(curve);
             }
             }
 
 
@@ -286,6 +443,58 @@ namespace BansheeEditor
             }
             }
         }
         }
 
 
+        private bool TryGetCurve(string path, out EdAnimationCurve curve)
+        {
+            int index = path.LastIndexOf(".");
+            string parentPath;
+            string subPathSuffix = null;
+            if (index == -1)
+            {
+                parentPath = path;
+            }
+            else
+            {
+                parentPath = path.Substring(0, index);
+                subPathSuffix = path.Substring(index, path.Length - index);
+            }
+
+            FieldCurves fieldCurves;
+            if (curves.TryGetValue(parentPath, out fieldCurves))
+            {
+                if (!string.IsNullOrEmpty(subPathSuffix))
+                {
+                    if (subPathSuffix == ".x" || subPathSuffix == ".r")
+                    {
+                        curve = fieldCurves.curves[0];
+                        return true;
+                    }
+                    else if (subPathSuffix == ".y" || subPathSuffix == ".g")
+                    {
+                        curve = fieldCurves.curves[1];
+                        return true;
+                    }
+                    else if (subPathSuffix == ".z" || subPathSuffix == ".b")
+                    {
+                        curve = fieldCurves.curves[2];
+                        return true;
+                    }
+                    else if (subPathSuffix == ".w" || subPathSuffix == ".a")
+                    {
+                        curve = fieldCurves.curves[3];
+                        return true;
+                    }
+                }
+                else
+                {
+                    curve = fieldCurves.curves[0];
+                    return true;
+                }
+            }
+
+            curve = null;
+            return false;
+        }
+
         private void OnFieldAdded(string path, SerializableProperty.FieldType type)
         private void OnFieldAdded(string path, SerializableProperty.FieldType type)
         {
         {
             guiFieldDisplay.AddField(path);
             guiFieldDisplay.AddField(path);
@@ -294,56 +503,82 @@ namespace BansheeEditor
             {
             {
                 case SerializableProperty.FieldType.Vector4:
                 case SerializableProperty.FieldType.Vector4:
                 {
                 {
-                    string[] subPaths = { ".x", ".y", ".z", ".w" };
+                    FieldCurves fieldCurves = new FieldCurves();
+                    fieldCurves.type = type;
+                    fieldCurves.curves = new EdAnimationCurve[4];
 
 
+                    string[] subPaths = { ".x", ".y", ".z", ".w" };
                     for (int i = 0; i < subPaths.Length; i++)
                     for (int i = 0; i < subPaths.Length; i++)
                     {
                     {
                         string subFieldPath = path + subPaths[i];
                         string subFieldPath = path + subPaths[i];
-                        curves[subFieldPath] = new EdAnimationCurve();
+                        fieldCurves.curves[i] = new EdAnimationCurve();
                         selectedFields.Add(subFieldPath);
                         selectedFields.Add(subFieldPath);
                     }
                     }
+
+                    curves[path] = fieldCurves;
                 }
                 }
                     break;
                     break;
                 case SerializableProperty.FieldType.Vector3:
                 case SerializableProperty.FieldType.Vector3:
                     {
                     {
-                        string[] subPaths = { ".x", ".y", ".z" };
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[3];
 
 
+                        string[] subPaths = { ".x", ".y", ".z" };
                         for (int i = 0; i < subPaths.Length; i++)
                         for (int i = 0; i < subPaths.Length; i++)
                         {
                         {
                             string subFieldPath = path + subPaths[i];
                             string subFieldPath = path + subPaths[i];
-                            curves[subFieldPath] = new EdAnimationCurve();
+                            fieldCurves.curves[i] = new EdAnimationCurve();
                             selectedFields.Add(subFieldPath);
                             selectedFields.Add(subFieldPath);
                         }
                         }
+
+                        curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
                 case SerializableProperty.FieldType.Vector2:
                 case SerializableProperty.FieldType.Vector2:
                     {
                     {
-                        string[] subPaths = { ".x", ".y" };
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[2];
 
 
+                        string[] subPaths = { ".x", ".y" };
                         for (int i = 0; i < subPaths.Length; i++)
                         for (int i = 0; i < subPaths.Length; i++)
                         {
                         {
                             string subFieldPath = path + subPaths[i];
                             string subFieldPath = path + subPaths[i];
-                            curves[subFieldPath] = new EdAnimationCurve();
+                            fieldCurves.curves[i] = new EdAnimationCurve();
                             selectedFields.Add(subFieldPath);
                             selectedFields.Add(subFieldPath);
                         }
                         }
+
+                        curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
                 case SerializableProperty.FieldType.Color:
                 case SerializableProperty.FieldType.Color:
                     {
                     {
-                        string[] subPaths = { ".r", ".g", ".b", ".a" };
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[4];
 
 
+                        string[] subPaths = { ".r", ".g", ".b", ".a" };
                         for (int i = 0; i < subPaths.Length; i++)
                         for (int i = 0; i < subPaths.Length; i++)
                         {
                         {
                             string subFieldPath = path + subPaths[i];
                             string subFieldPath = path + subPaths[i];
-                            curves[subFieldPath] = new EdAnimationCurve();
+                            fieldCurves.curves[i] = new EdAnimationCurve();
                             selectedFields.Add(subFieldPath);
                             selectedFields.Add(subFieldPath);
                         }
                         }
+
+                        curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
                 default: // Primitive type
                 default: // Primitive type
                     {
                     {
-                        curves[path] = new EdAnimationCurve();
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[1];
+
+                        fieldCurves.curves[0] = new EdAnimationCurve();
                         selectedFields.Add(path);
                         selectedFields.Add(path);
+
+                        curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
             }
             }
@@ -369,6 +604,15 @@ namespace BansheeEditor
             return true;
             return true;
         }
         }
 
 
+        private string GetSubPathParent(string path)
+        {
+            int index = path.LastIndexOf(".");
+            if (index == -1)
+                return path;
+
+            return path.Substring(0, index);
+        }
+
         private void OnFieldSelected(string path)
         private void OnFieldSelected(string path)
         {
         {
             if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
             if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
@@ -390,7 +634,7 @@ namespace BansheeEditor
             for (int i = 0; i < selectedFields.Count; i++)
             for (int i = 0; i < selectedFields.Count; i++)
             {
             {
                 selectedFields.Remove(selectedFields[i]);
                 selectedFields.Remove(selectedFields[i]);
-                curves.Remove(selectedFields[i]);
+                curves.Remove(GetSubPathParent(selectedFields[i]));
             }
             }
 
 
             List<string> existingFields = new List<string>();
             List<string> existingFields = new List<string>();
@@ -407,6 +651,43 @@ namespace BansheeEditor
         {
         {
             Rebuild();
             Rebuild();
         }
         }
+
+        private void OnFrameSelected(int frameIdx)
+        {
+            SetCurrentFrame(frameIdx);
+        }
+    }
+
+    /// <summary>
+    /// Drop down window that displays options used by the animation window.
+    /// </summary>
+    [DefaultSize(100, 50)]
+    internal class AnimationOptions : DropDownWindow
+    {
+        private AnimationWindow parent;
+
+        /// <summary>
+        /// Initializes the drop down window by creating the necessary GUI. Must be called after construction and before
+        /// use.
+        /// </summary>
+        /// <param name="parent">Animation window that this drop down window is a part of.</param>
+        internal void Initialize(AnimationWindow parent)
+        {
+            this.parent = parent;
+
+            GUIIntField fpsField = new GUIIntField(new LocEdString("FPS"), 40);
+            fpsField.Value = parent.FPS;
+            fpsField.OnChanged += x => { parent.FPS = x; };
+            
+            GUILayoutY vertLayout = GUI.AddLayoutY();
+
+            vertLayout.AddFlexibleSpace();
+            GUILayoutX contentLayout = vertLayout.AddLayoutX();
+            contentLayout.AddFlexibleSpace();
+            contentLayout.AddElement(fpsField);
+            contentLayout.AddFlexibleSpace();
+            vertLayout.AddFlexibleSpace();
+        }
     }
     }
 
 
     /** @} */
     /** @} */