Преглед изворни кода

Animated property display for animation editor (WIP)

BearishSun пре 9 година
родитељ
комит
f1329abe7e

BIN
Data/Raw/Editor/Skin/SelectionLblAlt.png


+ 1 - 0
Source/BansheeEditor/Include/BsBuiltinEditorResources.h

@@ -426,6 +426,7 @@ namespace BansheeEngine
 
 		static const WString SelectionAreaTex;
 		static const WString SelectionBgTex;
+		static const WString SelectionLblAltTex;
 
 		static const WString TextureDropTex;
 		static const WString TextureDropOnTex;

+ 19 - 0
Source/BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -241,6 +241,7 @@ namespace BansheeEngine
 
 	const WString BuiltinEditorResources::SelectionAreaTex = L"SelectionHighlight.png";
 	const WString BuiltinEditorResources::SelectionBgTex = L"SelectionBg.psd";
+	const WString BuiltinEditorResources::SelectionLblAltTex = L"SelectionLblAlt.png";
 
 	const WString BuiltinEditorResources::TextureDropTex = L"TextureDrop.png";
 	const WString BuiltinEditorResources::TextureDropOnTex = L"TextureDropHover.png";
@@ -1845,6 +1846,24 @@ namespace BansheeEngine
 
 		skin->setStyle("SelectableLabel", selectableLabelStyle);
 
+		// Selectable label with an alternate background
+		GUIElementStyle selectableLabelAltStyle;
+		selectableLabelAltStyle.normal.texture = getGUITexture(SelectionLblAltTex);
+		selectableLabelAltStyle.hover.texture = selectableLabelAltStyle.normal.texture;
+		selectableLabelAltStyle.active.texture = selectableLabelAltStyle.normal.texture;
+		selectableLabelAltStyle.normalOn.texture = getGUITexture(SelectionBgTex);
+		selectableLabelAltStyle.hoverOn.texture = selectableLabelAltStyle.normalOn.texture;
+		selectableLabelAltStyle.activeOn.texture = selectableLabelAltStyle.normalOn.texture;
+		selectableLabelAltStyle.fixedHeight = true;
+		selectableLabelAltStyle.height = 11;
+		selectableLabelAltStyle.minWidth = 10;
+		selectableLabelAltStyle.font = defaultFont;
+		selectableLabelAltStyle.fontSize = DefaultFontSize;
+		selectableLabelAltStyle.textHorzAlign = THA_Left;
+		selectableLabelAltStyle.normal.textColor = TextNormalColor;
+
+		skin->setStyle("SelectableLabelAlt", selectableLabelAltStyle);
+
 		// Scroll area background
 		GUIElementStyle scrollAreaBg;
 		scrollAreaBg.normal.texture = getGUITexture(ScrollAreaBgTex);

+ 1 - 0
Source/MBansheeEditor/GUI/EditorStyles.cs

@@ -31,6 +31,7 @@ namespace BansheeEditor
         public const string ColorSlider2DHandle = "ColorSlider2DHandle";
         public const string SelectionArea = "SelectionArea";
         public const string SelectableLabel = "SelectableLabel";
+        public const string SelectableLabelAlt = "SelectableLabelAlt";
         public const string ScrollAreaBg = "ScrollAreaBg";
         public const string InspectorTitleBg = "InspectorTitleBg";
         public const string InspectorContentBg = "InspectorContentBg";

+ 1 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -51,6 +51,7 @@
     <Compile Include="Utility\SerializedObject.cs" />
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
+    <Compile Include="Windows\Animation\GUIAnimFieldDisplay.cs" />
     <Compile Include="Windows\Animation\GUICurveDrawing.cs" />
     <Compile Include="Windows\Animation\GUICurveEditor.cs" />
     <Compile Include="Windows\Animation\GUIFieldSelector.cs" />

+ 377 - 0
Source/MBansheeEditor/Windows/Animation/GUIAnimFieldDisplay.cs

@@ -0,0 +1,377 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Collections.Generic;
+using System.Text;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup AnimationEditor
+     *  @{
+     */
+
+    internal class GUIAnimFieldDisplay
+    {
+        private SceneObject root;
+        private GUIScrollArea scrollArea;
+        private List<string> paths = new List<string>();
+
+        private GUIAnimFieldEntry[] fields;
+
+        public GUIAnimFieldDisplay(GUILayout layout, SceneObject root)
+        {
+            this.root = root;
+
+            scrollArea = new GUIScrollArea();
+            layout.AddElement(scrollArea);
+        }
+
+        public Action<string[]> OnSelectionChanged;
+
+        public void SetFields(string[] paths)
+        {
+            this.paths.Clear();
+            this.paths.AddRange(paths);
+
+            Rebuild();
+        }
+
+        public void AddField(string path)
+        {
+            paths.Add(path);
+
+            Rebuild();
+        }
+
+        private SerializableProperty FindProperty(string path)
+        {
+            if (string.IsNullOrEmpty(path) || root == null)
+                return null;
+
+            string[] entries = path.Split('/');
+
+            // Find scene object referenced by the path
+            SceneObject so = null;
+            int pathIdx = 0;
+            for (; pathIdx < entries.Length; pathIdx++)
+            {
+                string entry = entries[pathIdx];
+
+                // Not a scene object, break
+                if (entry[0] != '!')
+                    break;
+
+                if (pathIdx == 0)
+                    so = root;
+                else
+                {
+                    string childName = entry.Substring(1, entry.Length - 1);
+                    so = so.FindChild(childName);
+
+                    if (so == null)
+                        break;
+                }
+            }
+
+            // Cannot find scene object with specified hierarchy & name
+            if (so == null)
+                return null;
+
+            pathIdx++;
+            if (pathIdx >= entries.Length)
+                return null;
+
+            // If path is referencing a component, find it
+            Component component = null;
+            {
+                string entry = entries[pathIdx];
+                if (entry[0] == ':')
+                {
+                    string componentName = entry.Substring(1, entry.Length - 1);
+
+                    Component[] components = so.GetComponents();
+                    component = Array.Find(components, x => x.GetType().Name == componentName);
+
+                    // Cannot find component with specified type
+                    if (component == null)
+                        return null;
+                }
+            }
+
+            // Look for a field within a component
+            if (component != null)
+            {
+                pathIdx++;
+                if (pathIdx >= entries.Length)
+                    return null;
+
+                SerializableObject componentObj = new SerializableObject(component);
+
+                StringBuilder pathBuilder = new StringBuilder();
+                for(; pathIdx < entries.Length; pathIdx++)
+                    pathBuilder.Append(entries[pathIdx] + "/");
+
+                return componentObj.FindProperty(pathBuilder.ToString());
+            }
+            else // Field is one of the builtin ones on the SceneObject itself
+            {
+                if ((pathIdx + 1) < entries.Length)
+                    return null;
+
+                string entry = entries[pathIdx];
+                if (entry == "Position")
+                {
+                    SerializableProperty property = new SerializableProperty(
+                        SerializableProperty.FieldType.Vector3,
+                        typeof(Vector3),
+                        () => so.LocalPosition, 
+                        (x) => so.LocalPosition = (Vector3)x);
+
+                    return property;
+                }
+                else if (entry == "Rotation")
+                {
+                    SerializableProperty property = new SerializableProperty(
+                        SerializableProperty.FieldType.Vector3,
+                        typeof(Vector3),
+                        () => so.LocalRotation.ToEuler(), 
+                        (x) => so.LocalRotation = Quaternion.FromEuler((Vector3)x));
+
+                    return property;
+                }
+                else if (entry == "Scale")
+                {
+                    SerializableProperty property = new SerializableProperty(
+                        SerializableProperty.FieldType.Vector3,
+                        typeof(Vector3),
+                        () => so.LocalScale, 
+                        (x) => so.LocalScale = (Vector3)x);
+
+                    return property;
+                }
+
+                return null;
+            }
+        }
+
+        private void Rebuild()
+        {
+            scrollArea.Layout.Clear();
+            fields = null;
+
+            if (paths == null || root == null)
+                return;
+
+            fields = new GUIAnimFieldEntry[paths.Count];
+            for (int i = 0; i < paths.Count; i++)
+            {
+                SerializableProperty property = FindProperty(paths[i]);
+                if (property != null)
+                {
+                    switch (property.Type)
+                    {
+                        case SerializableProperty.FieldType.Vector2:
+                            fields[i] = new GUIAnimVec2Entry(scrollArea.Layout, paths[i]);
+                            break;
+                        case SerializableProperty.FieldType.Vector3:
+                            fields[i] = new GUIAnimVec3Entry(scrollArea.Layout, paths[i]);
+                            break;
+                        case SerializableProperty.FieldType.Vector4:
+                            fields[i] = new GUIAnimVec4Entry(scrollArea.Layout, paths[i]);
+                            break;
+                        case SerializableProperty.FieldType.Color:
+                            fields[i] = new GUIAnimColorEntry(scrollArea.Layout, paths[i]);
+                            break;
+                        case SerializableProperty.FieldType.Bool:
+                        case SerializableProperty.FieldType.Int:
+                        case SerializableProperty.FieldType.Float:
+                            fields[i] = new GUIAnimFieldEntry(scrollArea.Layout, paths[i]);
+                            break;
+                    }
+                }
+                else
+                {
+                    // TODO - Add special field type for missing properties
+                }
+            }
+        }
+    }
+
+    internal class GUIAnimFieldEntry
+    {
+        private const int MAX_PATH_LENGTH = 20;
+        protected const int INDENT_AMOUNT = 10;
+
+        private GUIToggle toggle;
+        protected string path;
+
+        public Action<string, bool> OnSelectionChanged;
+
+        public GUIAnimFieldEntry(GUILayout layout, string path)
+        {
+            toggle = new GUIToggle(GetDisplayName(path), EditorStyles.SelectableLabel);
+            layout.AddElement(toggle);
+
+            // TODO - Show current value (if not complex)
+            // TODO - Button to remove properties
+
+            this.path = path;
+        }
+
+        public virtual void Toggle(bool on)
+        { }
+
+        private static string GetDisplayName(string path)
+        {
+            if (string.IsNullOrEmpty(path))
+                return "";
+
+            string soName;
+            string compName;
+            string propertyPath;
+
+            GetNames(path, out soName, out compName, out propertyPath);
+
+            if (soName == null || propertyPath == null)
+                return "";
+
+            string truncatedPropPath;
+            if (propertyPath.Length > MAX_PATH_LENGTH)
+                truncatedPropPath = "..." + propertyPath.Substring(propertyPath.Length - MAX_PATH_LENGTH);
+            else
+                truncatedPropPath = propertyPath;
+
+            if (compName != null)
+                return soName + "(" + compName + ")." + truncatedPropPath;
+            else
+                return soName + "." + truncatedPropPath;
+        }
+
+        private static void GetNames(string path, out string soName, out string compName, out string propertyPath)
+        {
+            string[] entries = path.Split('/');
+
+            // Find name of the last scene object in the path
+            int pathIdx = 0;
+            for (; pathIdx < entries.Length; pathIdx++)
+            {
+                string entry = entries[pathIdx];
+
+                // Not a scene object, break
+                if (entry[0] != '!')
+                    break;
+            }
+
+            if (pathIdx >= entries.Length)
+            {
+                soName = null;
+                compName = null;
+                propertyPath = null;
+
+                return;
+            }
+
+            soName = entries[pathIdx].Substring(1, entries[pathIdx].Length - 1);
+
+            pathIdx++;
+            if (pathIdx >= entries.Length)
+            {
+                compName = null;
+                propertyPath = null;
+                return;
+            }
+
+            // If path is referencing a component, find it
+            {
+                string entry = entries[pathIdx];
+                if (entry[0] == ':')
+                    compName = entry.Substring(1, entry.Length - 1);
+                else
+                    compName = null;
+            }
+
+            // Look for a field name
+            if (compName != null)
+            {
+                pathIdx++;
+                if (pathIdx >= entries.Length)
+                {
+                    propertyPath = null;
+                    return;
+                }
+            }
+
+            StringBuilder pathBuilder = new StringBuilder();
+            for (; pathIdx < entries.Length; pathIdx++)
+                pathBuilder.Append(entries[pathIdx] + "/");
+
+            propertyPath = pathBuilder.ToString();
+        }
+    }
+
+    internal class GUIAnimComplexEntry : GUIAnimFieldEntry
+    {
+        private GUIToggle[] children;
+        private GUILayout childLayout;
+
+        protected GUIAnimComplexEntry(GUILayout layout, string path, string[] childEntries)
+            : base(layout, path)
+        {
+            childLayout = layout.AddLayoutX();
+            childLayout.AddSpace(INDENT_AMOUNT);
+
+            // TODO - Foldout to expand/collapse child layout
+
+            GUILayout indentLayout = childLayout.AddLayoutY();
+
+            children = new GUIToggle[childEntries.Length];
+            for (int i = 0; i < childEntries.Length; i++)
+            {
+                int index = i;
+
+                children[i] = new GUIToggle(new LocEdString(childEntries[i]), EditorStyles.SelectableLabel);
+                children[i].OnToggled += x => { OnSelectionChanged?.Invoke(path + childEntries[index], x); };
+
+                indentLayout.AddElement(children[i]);
+            }
+
+            Toggle(false);
+        }
+
+        public override void Toggle(bool on)
+        {
+            childLayout.Active = on;
+        }
+    }
+
+    internal class GUIAnimVec2Entry : GUIAnimComplexEntry
+    {
+        public GUIAnimVec2Entry(GUILayout layout, string path)
+            : base(layout, path,  new[] { ".x", ".y" })
+        { }
+    }
+
+    internal class GUIAnimVec3Entry : GUIAnimComplexEntry
+    {
+        public GUIAnimVec3Entry(GUILayout layout, string path)
+            : base(layout, path, new[] { ".x", ".y", ".z" })
+        { }
+    }
+
+    internal class GUIAnimVec4Entry : GUIAnimComplexEntry
+    {
+        public GUIAnimVec4Entry(GUILayout layout, string path)
+            : base(layout, path,  new[] { ".x", ".y", ".z", ".w" })
+        { }
+    }
+
+    internal class GUIAnimColorEntry : GUIAnimComplexEntry
+    {
+        public GUIAnimColorEntry(GUILayout layout, string path)
+            : base(layout, path, new[] { ".r", ".g", ".b", ".a" })
+        { }
+    }
+
+    /** @} */
+}

+ 7 - 2
Source/MBansheeEditor/Windows/Animation/GUICurveEditor.cs

@@ -6,7 +6,7 @@ using BansheeEngine;
 
 namespace BansheeEditor
 {
-    /** @addtogroup Windows
+    /** @addtogroup AnimationEditor
      *  @{
      */
 
@@ -225,6 +225,7 @@ namespace BansheeEditor
                                     KeyFrame[] keyFrames = curve.KeyFrames;
 
                                     DraggedKeyframes newEntry = new DraggedKeyframes();
+                                    newEntry.curveIdx = selectedEntry.curveIdx;
                                     draggedKeyframes.Add(newEntry);
 
                                     foreach (var keyframeIdx in selectedEntry.keyIndices)
@@ -558,7 +559,11 @@ namespace BansheeEditor
                 if (curveIdx == -1)
                 {
                     curveIdx = selectedKeyframes.Count;
-                    selectedKeyframes.Add(new SelectedKeyframes());
+
+                    SelectedKeyframes newKeyframes = new SelectedKeyframes();
+                    newKeyframes.curveIdx = keyframeRef.curveIdx;
+
+                    selectedKeyframes.Add(newKeyframes);
                 }
 
                 selectedKeyframes[curveIdx].keyIndices.Add(keyframeRef.keyIdx);

+ 17 - 11
Source/MBansheeEditor/Windows/Animation/GUIFieldSelector.cs

@@ -19,9 +19,6 @@ namespace BansheeEditor
     {
         private const int INDENT_AMOUNT = 5;
 
-        private SpriteTexture ICON_SO; // TODO
-        private SpriteTexture ICON_COMPONENT; // TODO
-
         private GUILayoutY mainLayout;
 
         private SceneObject rootSO;
@@ -59,7 +56,10 @@ namespace BansheeEditor
         /// <summary>
         /// Triggers when the user selects a field. The subscriber will be receive a scene object the field is part of,
         /// component the field is part of, and a path to the field, each entry separated by "/". Component can be null
-        /// in which case it is assumed the field is part of the SceneObject itself.
+        /// in which case it is assumed the field is part of the SceneObject itself. Scene object names are always prefixed
+        /// with "!", components are always prefixed with ":", while field entries have no prefix.
+        /// 
+        /// For example: !My Scene Object/:Camera/path/to/field
         /// </summary>
         public Action<SceneObject, Component, string> OnElementSelected;
 
@@ -98,13 +98,17 @@ namespace BansheeEditor
         /// <param name="parent">Row element under which to create the new rows.</param>
         private void AddSceneObjectRows(Element parent)
         {
+            string soName = "!" + parent.so.Name;
             Component[] components = parent.so.GetComponents();
 
             parent.children = new Element[components.Length + 2];
 
+            SpriteTexture soIcon = EditorBuiltin.GetEditorIcon(EditorIcon.SceneObject);
+            SpriteTexture compIcon = EditorBuiltin.GetEditorIcon(EditorIcon.Component);
+
             // Add transform
-            parent.children[0] = AddFoldoutRow(mainLayout, ICON_COMPONENT, "Transform", parent.so, 
-                null, "", ToggleTransformFoldout);
+            parent.children[0] = AddFoldoutRow(mainLayout, soIcon, "Transform", parent.so, 
+                null, parent.path + "/" + soName, ToggleTransformFoldout);
 
             // Add components
             for (int i = 0; i < components.Length; i++)
@@ -116,15 +120,16 @@ namespace BansheeEditor
                     SerializableObject componentObject = new SerializableObject(childComponent.GetType(), null);
                     ToggleObjectFoldout(toggleParent, componentObject, expand);
                 };
-
+                
                 string name = childComponent.GetType().Name;
-                parent.children[i + 1] = AddFoldoutRow(mainLayout, ICON_COMPONENT, name, parent.so, childComponent, "", 
+                string path = parent.path + "/" + soName + "/:" + name;
+                parent.children[i + 1] = AddFoldoutRow(mainLayout, compIcon, name, parent.so, childComponent, path,
                     toggleCallback);
             }
 
             // Add children
-            parent.children[parent.children.Length - 1] = AddFoldoutRow(mainLayout, ICON_SO, "Children", parent.so, null, "",
-                ToggleChildFoldout);
+            parent.children[parent.children.Length - 1] = AddFoldoutRow(mainLayout, soIcon, "Children", parent.so, null,
+                parent.path + "/" + soName, ToggleChildFoldout);
         }
 
         /// <summary>
@@ -291,7 +296,8 @@ namespace BansheeEditor
                 {
                     SceneObject child = parent.so.GetChild(i);
 
-                    parent.children[i] = AddFoldoutRow(parent.childLayout, ICON_SO, child.Name, child, null, "",
+                    SpriteTexture soIcon = EditorBuiltin.GetEditorIcon(EditorIcon.SceneObject);
+                    parent.children[i] = AddFoldoutRow(parent.childLayout, soIcon, child.Name, child, null, parent.path,
                         ToggleSceneObjectFoldout);
                 }
             }