소스 검색

Update inspector as selection changes
Update inspector as SceneObject components are added or removed

Marko Pintera 10 년 전
부모
커밋
06a238cab0

+ 71 - 5
BansheeEditor/Include/BsHandleManager.h

@@ -6,27 +6,97 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	/**
+	 * @brief	The central place for interacting with and drawing handles.
+	 */
 	class BS_ED_EXPORT HandleManager : public Module<HandleManager>
 	class BS_ED_EXPORT HandleManager : public Module<HandleManager>
 	{
 	{
 	public:
 	public:
 		HandleManager();
 		HandleManager();
 		virtual ~HandleManager();
 		virtual ~HandleManager();
 
 
+		/**
+		 * @brief	To be called every frame. Updates interactable handle sliders based on provided input
+		 *			and queues handles for drawing.
+		 *
+		 * @param	camera		Camera that the input positions are relative to, and destination to draw the handles to.
+		 * @param	inputPos	Position of the pointer, relative to the provided camera viewport.
+		 * @param	inputDelta	Determines pointer movement since last call to this method.
+		 */
 		void update(const CameraHandlerPtr& camera, const Vector2I& inputPos, const Vector2I& inputDelta);
 		void update(const CameraHandlerPtr& camera, const Vector2I& inputPos, const Vector2I& inputDelta);
+
+		/**
+		 * @brief	Select a handle slider at the specified location, if there is any under the pointer. Makes
+		 *			the selected slider active and draggable.
+		 *
+		 * @param	camera		Camera that the input positions are relative to, and destination to draw the handles to.
+		 * @param	inputPos	Position of the pointer, relative to the provided camera viewport.
+		 */
 		void trySelect(const CameraHandlerPtr& camera, const Vector2I& inputPos);
 		void trySelect(const CameraHandlerPtr& camera, const Vector2I& inputPos);
+
+		/**
+		 * @brief	Clears the currently selected/active handle slider.
+		 */
 		void clearSelection();
 		void clearSelection();
+
+		/**
+		 * @brief	Is any handle slider selected/active.
+		 */
 		bool isHandleActive() const;
 		bool isHandleActive() const;
 
 
+		/**
+		 * @brief	Returns the manager that can be used for interacting with handle sliders.
+		 */
 		HandleSliderManager& getSliderManager() const { return *mSliderManager; }
 		HandleSliderManager& getSliderManager() const { return *mSliderManager; }
+
+		/**
+		 * @brief	Returns the manager that can be used for drawing handle geometry.
+		 */
 		HandleDrawManager& getDrawManager() const { return *mDrawManager; }
 		HandleDrawManager& getDrawManager() const { return *mDrawManager; }
 
 
+		/**
+		 * @brief	Returns the uniform size for a handle rendered in @p camera, at the world
+		 *			position @p handlePos. The handles will be scaled so that they appear
+		 *			the same size regardless of distance from camera.
+		 */
 		float getHandleSize(const CameraHandlerPtr& camera, const Vector3& handlePos) const;
 		float getHandleSize(const CameraHandlerPtr& camera, const Vector3& handlePos) const;
 
 
+		/**
+		 * @brief	Sets the default handle size. This controls the uniform scale returned from
+		 *			::getHandleSize method.
+		 */
 		void setDefaultHandleSize(float value) { mDefaultHandleSize = value; }
 		void setDefaultHandleSize(float value) { mDefaultHandleSize = value; }
+
+		/**
+		 * @brief	Sets editor settings that will be used for controlling various 
+		 *			handle behaviour.
+		 */
 		void setSettings(const EditorSettingsPtr& settings);
 		void setSettings(const EditorSettingsPtr& settings);
 
 
 	protected:
 	protected:
-		void updateFromProjectSettings();
+		/**
+		 * @brief	Updates the internal properties from editor settings.
+		 */
+		void updateFromEditorSettings();
+
+		/**
+		 * @brief	Called during handle update. Allows handle sliders to be created or 
+		 *			destroyed before any input is handled.
+		 */
+		virtual void refreshHandles() = 0;
+
+		/**
+		 * @brief	Called during handle update after handle input is processed.
+		 *			Allows implementation to respond to delta values calculated in sliders
+		 *			due to input.
+		 */
+		virtual void triggerHandles() = 0;
+
+		/**
+		 * @brief	Called during handle update. Allows implementation to
+		 *			queue handle draw commands.
+		 */
+		virtual void queueDrawCommands() = 0;
 
 
 		HandleSliderManager* mSliderManager;
 		HandleSliderManager* mSliderManager;
 		HandleDrawManager* mDrawManager;
 		HandleDrawManager* mDrawManager;
@@ -35,9 +105,5 @@ namespace BansheeEngine
 
 
 		EditorSettingsPtr mSettings;
 		EditorSettingsPtr mSettings;
 		UINT32 mSettingsHash;
 		UINT32 mSettingsHash;
-
-		virtual void refreshHandles() = 0;
-		virtual void triggerHandles() = 0;
-		virtual void queueDrawCommands() = 0;
 	};
 	};
 }
 }

+ 1 - 0
BansheeEditor/Include/BsSelection.h

@@ -23,6 +23,7 @@ namespace BansheeEngine
 		void clearSceneSelection();
 		void clearSceneSelection();
 		void clearResourceSelection();
 		void clearResourceSelection();
 
 
+		Event<void(const Vector<HSceneObject>&, const Vector<Path>&)> onSelectionChanged;
 	private:
 	private:
 		void sceneSelectionChanged();
 		void sceneSelectionChanged();
 		void resourceSelectionChanged();
 		void resourceSelectionChanged();

+ 3 - 3
BansheeEditor/Source/BsHandleManager.cpp

@@ -28,7 +28,7 @@ namespace BansheeEngine
 	void HandleManager::update(const CameraHandlerPtr& camera, const Vector2I& inputPos, const Vector2I& inputDelta)
 	void HandleManager::update(const CameraHandlerPtr& camera, const Vector2I& inputPos, const Vector2I& inputDelta)
 	{
 	{
 		if (mSettings != nullptr && mSettingsHash != mSettings->getHash())
 		if (mSettings != nullptr && mSettingsHash != mSettings->getHash())
-			updateFromProjectSettings();
+			updateFromEditorSettings();
 
 
 		refreshHandles();
 		refreshHandles();
 		mSliderManager->update(camera, inputPos, inputDelta);
 		mSliderManager->update(camera, inputPos, inputDelta);
@@ -42,10 +42,10 @@ namespace BansheeEngine
 	{
 	{
 		mSettings = settings;
 		mSettings = settings;
 
 
-		updateFromProjectSettings();
+		updateFromEditorSettings();
 	}
 	}
 
 
-	void HandleManager::updateFromProjectSettings()
+	void HandleManager::updateFromEditorSettings()
 	{
 	{
 		setDefaultHandleSize(mSettings->getHandleSize());
 		setDefaultHandleSize(mSettings->getHandleSize());
 
 

+ 14 - 0
BansheeEditor/Source/BsSelection.cpp

@@ -34,6 +34,8 @@ namespace BansheeEngine
 		GUISceneTreeView* sceneTreeView = SceneTreeViewLocator::instance();
 		GUISceneTreeView* sceneTreeView = SceneTreeViewLocator::instance();
 		if (sceneTreeView != nullptr)
 		if (sceneTreeView != nullptr)
 			sceneTreeView->setSelection(sceneObjects);
 			sceneTreeView->setSelection(sceneObjects);
+
+		onSelectionChanged(mSelectedSceneObjects, Vector<Path>());
 	}
 	}
 
 
 	const Vector<Path>& Selection::getResourcePaths() const
 	const Vector<Path>& Selection::getResourcePaths() const
@@ -44,6 +46,8 @@ namespace BansheeEngine
 	void Selection::setResourcePaths(const Vector<Path>& paths)
 	void Selection::setResourcePaths(const Vector<Path>& paths)
 	{
 	{
 		mSelectedResourcePaths = paths;
 		mSelectedResourcePaths = paths;
+
+		onSelectionChanged(Vector<HSceneObject>(), mSelectedResourcePaths);
 	}
 	}
 
 
 	Vector<String> Selection::getResourceUUIDs() const
 	Vector<String> Selection::getResourceUUIDs() const
@@ -75,6 +79,8 @@ namespace BansheeEngine
 		GUIResourceTreeView* resourceTreeView = ResourceTreeViewLocator::instance();
 		GUIResourceTreeView* resourceTreeView = ResourceTreeViewLocator::instance();
 		if (resourceTreeView != nullptr)
 		if (resourceTreeView != nullptr)
 			resourceTreeView->setSelection(mSelectedResourcePaths);
 			resourceTreeView->setSelection(mSelectedResourcePaths);
+
+		onSelectionChanged(Vector<HSceneObject>(), mSelectedResourcePaths);
 	}
 	}
 
 
 	void Selection::clearSceneSelection()
 	void Selection::clearSceneSelection()
@@ -91,13 +97,21 @@ namespace BansheeEngine
 	{
 	{
 		GUISceneTreeView* sceneTreeView = SceneTreeViewLocator::instance();
 		GUISceneTreeView* sceneTreeView = SceneTreeViewLocator::instance();
 		if (sceneTreeView != nullptr)
 		if (sceneTreeView != nullptr)
+		{
 			mSelectedSceneObjects = sceneTreeView->getSelection();
 			mSelectedSceneObjects = sceneTreeView->getSelection();
+
+			onSelectionChanged(mSelectedSceneObjects, Vector<Path>());
+		}
 	}
 	}
 
 
 	void Selection::resourceSelectionChanged()
 	void Selection::resourceSelectionChanged()
 	{
 	{
 		GUIResourceTreeView* resourceTreeView = ResourceTreeViewLocator::instance();
 		GUIResourceTreeView* resourceTreeView = ResourceTreeViewLocator::instance();
 		if (resourceTreeView != nullptr)
 		if (resourceTreeView != nullptr)
+		{
 			mSelectedResourcePaths = resourceTreeView->getSelection();
 			mSelectedResourcePaths = resourceTreeView->getSelection();
+
+			onSelectionChanged(Vector<HSceneObject>(), mSelectedResourcePaths);
+		}
 	}
 	}
 }
 }

+ 141 - 17
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -6,15 +6,31 @@ namespace BansheeEditor
 {
 {
     internal sealed class InspectorWindow : EditorWindow
     internal sealed class InspectorWindow : EditorWindow
     {
     {
-        private class InspectorData
+        private enum InspectorType
+        {
+            SceneObject,
+            Resource,
+            Multiple,
+            None
+        }
+
+        private class InspectorComponent
         {
         {
             public GUIComponentFoldout foldout;
             public GUIComponentFoldout foldout;
             public GUIPanel panel;
             public GUIPanel panel;
             public Inspector inspector;
             public Inspector inspector;
             public bool expanded = true;
             public bool expanded = true;
+            public UInt64 instanceId;
+        }
+
+        private class InspectorResource
+        {
+            public GUIPanel panel;
+            public Inspector inspector;
         }
         }
 
 
-        private List<InspectorData> inspectorData = new List<InspectorData>();
+        private List<InspectorComponent> inspectorComponents = new List<InspectorComponent>();
+        private InspectorResource inspectorResource;
         private GUIScrollArea inspectorScrollArea;
         private GUIScrollArea inspectorScrollArea;
         private GUILayout inspectorLayout;
         private GUILayout inspectorLayout;
 
 
@@ -32,6 +48,9 @@ namespace BansheeEditor
         private GUIFloatField soScaleY;
         private GUIFloatField soScaleY;
         private GUIFloatField soScaleZ;
         private GUIFloatField soScaleZ;
 
 
+        private InspectorType currentType = InspectorType.None;
+        private Resource activeResource;
+
         [MenuItem("Windows/Inspector", ButtonModifier.CtrlAlt, ButtonCode.I)]
         [MenuItem("Windows/Inspector", ButtonModifier.CtrlAlt, ButtonCode.I)]
         private static void OpenInspectorWindow()
         private static void OpenInspectorWindow()
         {
         {
@@ -43,15 +62,37 @@ namespace BansheeEditor
             return "Inspector";
             return "Inspector";
         }
         }
 
 
-        internal void SetObjectToInspect(SceneObject so)
+        private void SetObjectToInspect(String resourcePath)
         {
         {
-            Clear();
+            activeResource = ProjectLibrary.Load<Resource>(resourcePath);
 
 
-            activeSO = so;
+            if (activeResource == null)
+                return;
 
 
-            if (activeSO == null)
+            currentType = InspectorType.Resource;
+
+            inspectorScrollArea = new GUIScrollArea();
+            GUI.AddElement(inspectorScrollArea);
+            inspectorLayout = inspectorScrollArea.Layout;
+
+            inspectorResource = new InspectorResource();
+            inspectorResource.panel = inspectorLayout.AddPanel();
+
+            inspectorResource.inspector = GetInspector(activeResource.GetType());
+            inspectorResource.inspector.Initialize(this, inspectorResource.panel, activeResource);
+            inspectorResource.inspector.Refresh();
+
+            inspectorLayout.AddFlexibleSpace();
+        }
+
+        private void SetObjectToInspect(SceneObject so)
+        {
+            if (so == null)
                 return;
                 return;
 
 
+            currentType = InspectorType.SceneObject;
+            activeSO = so;
+
             inspectorScrollArea = new GUIScrollArea();
             inspectorScrollArea = new GUIScrollArea();
             GUI.AddElement(inspectorScrollArea);
             GUI.AddElement(inspectorScrollArea);
             inspectorLayout = inspectorScrollArea.Layout;
             inspectorLayout = inspectorScrollArea.Layout;
@@ -64,7 +105,8 @@ namespace BansheeEditor
             Component[] allComponents = so.GetComponents();
             Component[] allComponents = so.GetComponents();
             for (int i = 0; i < allComponents.Length; i++)
             for (int i = 0; i < allComponents.Length; i++)
             {
             {
-                InspectorData data = new InspectorData();
+                InspectorComponent data = new InspectorComponent();
+                data.instanceId = allComponents[i].InstanceId;
 
 
                 data.foldout = new GUIComponentFoldout(allComponents[i].GetType().Name);
                 data.foldout = new GUIComponentFoldout(allComponents[i].GetType().Name);
                 inspectorLayout.AddElement(data.foldout);
                 inspectorLayout.AddElement(data.foldout);
@@ -76,15 +118,15 @@ namespace BansheeEditor
                 data.foldout.SetExpanded(true);
                 data.foldout.SetExpanded(true);
                 data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
                 data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
 
 
-                inspectorData.Add(data);
+                inspectorComponents.Add(data);
 
 
-                inspectorData[i].inspector.Refresh();
+                inspectorComponents[i].inspector.Refresh();
             }
             }
 
 
             inspectorLayout.AddFlexibleSpace();
             inspectorLayout.AddFlexibleSpace();
         }
         }
 
 
-        private void OnComponentFoldoutToggled(InspectorData inspectorData, bool expanded)
+        private void OnComponentFoldoutToggled(InspectorComponent inspectorData, bool expanded)
         {
         {
             inspectorData.expanded = expanded;
             inspectorData.expanded = expanded;
             inspectorData.inspector.SetVisible(expanded);
             inspectorData.inspector.SetVisible(expanded);
@@ -160,6 +202,8 @@ namespace BansheeEditor
             scaleLayout.AddSpace(10);
             scaleLayout.AddSpace(10);
             scaleLayout.AddElement(soScaleZ);
             scaleLayout.AddElement(soScaleZ);
             scaleLayout.AddFlexibleSpace();
             scaleLayout.AddFlexibleSpace();
+
+            inspectorLayout.AddSpace(15);
         }
         }
 
 
         private void RefreshSceneObjectFields(bool forceUpdate)
         private void RefreshSceneObjectFields(bool forceUpdate)
@@ -230,13 +274,84 @@ namespace BansheeEditor
             soScaleZ.Value = scale.z;
             soScaleZ.Value = scale.z;
         }
         }
 
 
+        private void OnInitialize()
+        {
+            Selection.OnSelectionChanged += OnSelectionChanged;
+        }
+
+        private void OnDestroy()
+        {
+            Selection.OnSelectionChanged -= OnSelectionChanged;
+        }
+
         private void OnEditorUpdate()
         private void OnEditorUpdate()
         {
         {
-            RefreshSceneObjectFields(false);
+            if (currentType == InspectorType.SceneObject)
+            {
+                Component[] allComponents = activeSO.GetComponents();
+                bool requiresRebuild = allComponents.Length != inspectorComponents.Count;
+
+                if (!requiresRebuild)
+                {
+                    for (int i = 0; i < inspectorComponents.Count; i++)
+                    {
+                        if (inspectorComponents[i].instanceId != allComponents[i].InstanceId)
+                        {
+                            requiresRebuild = true;
+                            break;
+                        }
+                    }
+                }
 
 
-            for (int i = 0; i < inspectorData.Count; i++)
+                if (requiresRebuild)
+                    SetObjectToInspect(activeSO);
+                else
+                {
+                    RefreshSceneObjectFields(false);
+
+                    for (int i = 0; i < inspectorComponents.Count; i++)
+                    {
+                        inspectorComponents[i].inspector.Refresh();
+                    }
+                }
+            }
+            else if (currentType == InspectorType.Resource)
             {
             {
-                inspectorData[i].inspector.Refresh();
+                inspectorResource.inspector.Refresh();
+            }
+        }
+
+        private void OnSelectionChanged(SceneObject[] objects, string[] paths)
+        {
+            Clear();
+
+            if (objects.Length == 0 && paths.Length == 0)
+            {
+                currentType = InspectorType.None;
+                inspectorScrollArea = new GUIScrollArea();
+                GUI.AddElement(inspectorScrollArea);
+                inspectorLayout = inspectorScrollArea.Layout;
+                inspectorLayout.AddElement(new GUILabel("No object selected"));
+                inspectorLayout.AddFlexibleSpace();
+            }
+            else if ((objects.Length + paths.Length) > 1)
+            {
+                currentType = InspectorType.None;
+                inspectorScrollArea = new GUIScrollArea();
+                GUI.AddElement(inspectorScrollArea);
+                inspectorLayout = inspectorScrollArea.Layout;
+                inspectorLayout.AddElement(new GUILabel("Multiple objects selected"));
+                inspectorLayout.AddFlexibleSpace();
+            }
+            else if (objects.Length == 1)
+            {
+                Debug.Log("Object selected: " + objects[0].Name);
+                SetObjectToInspect(objects[0]);
+            }
+            else if (paths.Length == 1)
+            {
+                Debug.Log("Path selected: " + paths[0]);
+                SetObjectToInspect(paths[0]);
             }
             }
         }
         }
 
 
@@ -247,13 +362,19 @@ namespace BansheeEditor
 
 
         internal void Clear()
         internal void Clear()
         {
         {
-            for (int i = 0; i < inspectorData.Count; i++)
+            for (int i = 0; i < inspectorComponents.Count; i++)
             {
             {
-                inspectorData[i].foldout.Destroy();
-                inspectorData[i].inspector.Destroy();
+                inspectorComponents[i].foldout.Destroy();
+                inspectorComponents[i].inspector.Destroy();
             }
             }
 
 
-            inspectorData.Clear();
+            inspectorComponents.Clear();
+
+            if (inspectorResource != null)
+            {
+                inspectorResource.inspector.Destroy();
+                inspectorResource = null;
+            }
 
 
             if (inspectorScrollArea != null)
             if (inspectorScrollArea != null)
             {
             {
@@ -274,6 +395,9 @@ namespace BansheeEditor
             soScaleX = null;
             soScaleX = null;
             soScaleY = null;
             soScaleY = null;
             soScaleZ = null;
             soScaleZ = null;
+
+            activeResource = null;
+            currentType = InspectorType.None;
         }
         }
 
 
         private void OnPositionChanged(int idx, float value)
         private void OnPositionChanged(int idx, float value)

+ 10 - 1
MBansheeEditor/Selection.cs

@@ -1,10 +1,13 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.CompilerServices;
 using BansheeEngine;
 using BansheeEngine;
 
 
 namespace BansheeEditor
 namespace BansheeEditor
 {
 {
     public sealed class Selection
     public sealed class Selection
     {
     {
+        public static Action<SceneObject[], string[]> OnSelectionChanged;
+
         public static SceneObject[] sceneObjects
         public static SceneObject[] sceneObjects
         {
         {
             get
             get
@@ -47,6 +50,12 @@ namespace BansheeEditor
             }
             }
         }
         }
 
 
+        private static void Internal_TriggerSelectionChanged(SceneObject[] objects, string[] paths)
+        {
+            if (OnSelectionChanged != null)
+                OnSelectionChanged(objects, paths);
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern void Internal_GetSceneObjectSelection(out SceneObject[] selection);
         internal static extern void Internal_GetSceneObjectSelection(out SceneObject[] selection);
 
 

+ 3 - 0
MBansheeEngine/GUI/GUILayout.cs

@@ -14,6 +14,9 @@ namespace BansheeEngine
 
 
         public void InsertElement(int index, GUIElement element)
         public void InsertElement(int index, GUIElement element)
         {
         {
+            if(index < 0 || index > GetNumChildren())
+                throw new ArgumentOutOfRangeException("index", index, "Index out of range.");
+
             if (element != null)
             if (element != null)
                 Internal_InsertElement(mCachedPtr, index, element.mCachedPtr);
                 Internal_InsertElement(mCachedPtr, index, element.mCachedPtr);
         }
         }

+ 11 - 1
MBansheeEngine/GameObject.cs

@@ -1,6 +1,16 @@
-namespace BansheeEngine
+using System;
+using System.Runtime.CompilerServices;
+
+namespace BansheeEngine
 {
 {
     public class GameObject : ScriptObject
     public class GameObject : ScriptObject
     {
     {
+        public UInt64 InstanceId
+        {
+            get { return Internal_GetInstanceId(mCachedPtr); }
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern UInt64 Internal_GetInstanceId(IntPtr thisPtr);
     }
     }
 }
 }

+ 5 - 5
SBansheeEditor/Include/BsScriptEditorWindow.h

@@ -45,10 +45,10 @@ namespace BansheeEngine
 		void onFocusChanged(bool inFocus);
 		void onFocusChanged(bool inFocus);
 		void onAssemblyRefreshStarted();
 		void onAssemblyRefreshStarted();
 
 
-		void _onManagedInstanceDeleted();
-		ScriptObjectBackup beginRefresh();
-		void endRefresh(const ScriptObjectBackup& backupData);
-		MonoObject* _createManagedInstance(bool construct);
+		void _onManagedInstanceDeleted() override;
+		ScriptObjectBackup beginRefresh() override;
+		void endRefresh(const ScriptObjectBackup& backupData) override;
+		MonoObject* _createManagedInstance(bool construct) override;
 
 
 		String mName;
 		String mName;
 		ScriptEditorWidget* mEditorWidget;
 		ScriptEditorWidget* mEditorWidget;
@@ -79,7 +79,7 @@ namespace BansheeEngine
 		~ScriptEditorWidget();
 		~ScriptEditorWidget();
 
 
 		bool createManagedInstance();
 		bool createManagedInstance();
-		void update();
+		void update() override;
 		void reloadMonoTypes(MonoClass* windowClass);
 		void reloadMonoTypes(MonoClass* windowClass);
 		void triggerOnInitialize();
 		void triggerOnInitialize();
 		void triggerOnDestroy();
 		void triggerOnDestroy();

+ 13 - 1
SBansheeEditor/Include/BsScriptSelection.h

@@ -8,9 +8,16 @@ namespace BansheeEngine
 	class BS_SCR_BED_EXPORT ScriptSelection : public ScriptObject<ScriptSelection>
 	class BS_SCR_BED_EXPORT ScriptSelection : public ScriptObject<ScriptSelection>
 	{
 	{
 	public:
 	public:
-		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "Selection")
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "Selection");
+
+		static void startUp();
+		static void shutDown();
 
 
 	private:
 	private:
+		ScriptSelection(MonoObject* instance);
+
+		static void onSelectionChanged(const Vector<HSceneObject>& sceneObjects, const Vector<Path>& resPaths);
+
 		static void internal_GetSceneObjectSelection(MonoArray** selection);
 		static void internal_GetSceneObjectSelection(MonoArray** selection);
 		static void internal_SetSceneObjectSelection(MonoArray* selection);
 		static void internal_SetSceneObjectSelection(MonoArray* selection);
 
 
@@ -19,5 +26,10 @@ namespace BansheeEngine
 
 
 		static void internal_GetResourcePathSelection(MonoArray** selection);
 		static void internal_GetResourcePathSelection(MonoArray** selection);
 		static void internal_SetResourcePathSelection(MonoArray* selection);
 		static void internal_SetResourcePathSelection(MonoArray* selection);
+
+		typedef void(__stdcall *OnSelectionChangedThunkDef) (MonoArray*, MonoArray*, MonoException**);
+		static OnSelectionChangedThunkDef OnSelectionChangedThunk;
+
+		static HEvent OnSelectionChangedConn;
 	};
 	};
 }
 }

+ 4 - 1
SBansheeEditor/Source/BsEditorScriptManager.cpp

@@ -16,7 +16,8 @@
 #include "BsTime.h"
 #include "BsTime.h"
 #include "BsMath.h"
 #include "BsMath.h"
 #include "BsEditorApplication.h"
 #include "BsEditorApplication.h"
-#include <BsTestOutput.h>
+#include "BsScriptSelection.h"
+#include "BsTestOutput.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -36,6 +37,7 @@ namespace BansheeEngine
 		ScriptProjectLibrary::startUp();
 		ScriptProjectLibrary::startUp();
 		MenuItemManager::startUp(ScriptAssemblyManager::instance());
 		MenuItemManager::startUp(ScriptAssemblyManager::instance());
 		ScriptFolderMonitorManager::startUp();
 		ScriptFolderMonitorManager::startUp();
+		ScriptSelection::startUp();
 
 
 		mOnDomainLoadConn = ScriptObjectManager::instance().onRefreshDomainLoaded.connect(std::bind(&EditorScriptManager::loadMonoTypes, this));
 		mOnDomainLoadConn = ScriptObjectManager::instance().onRefreshDomainLoaded.connect(std::bind(&EditorScriptManager::loadMonoTypes, this));
 		mOnAssemblyRefreshDoneConn = ScriptObjectManager::instance().onRefreshComplete.connect(std::bind(&EditorScriptManager::onAssemblyRefreshDone, this));
 		mOnAssemblyRefreshDoneConn = ScriptObjectManager::instance().onRefreshComplete.connect(std::bind(&EditorScriptManager::onAssemblyRefreshDone, this));
@@ -58,6 +60,7 @@ namespace BansheeEngine
 		mOnDomainLoadConn.disconnect();
 		mOnDomainLoadConn.disconnect();
 		mOnAssemblyRefreshDoneConn.disconnect();
 		mOnAssemblyRefreshDoneConn.disconnect();
 
 
+		ScriptSelection::shutDown();
 		ScriptFolderMonitorManager::shutDown();
 		ScriptFolderMonitorManager::shutDown();
 		MenuItemManager::shutDown();
 		MenuItemManager::shutDown();
 		ScriptProjectLibrary::shutDown();
 		ScriptProjectLibrary::shutDown();

+ 47 - 0
SBansheeEditor/Source/BsScriptSelection.cpp

@@ -1,6 +1,7 @@
 #include "BsScriptSelection.h"
 #include "BsScriptSelection.h"
 #include "BsScriptMeta.h"
 #include "BsScriptMeta.h"
 #include "BsMonoClass.h"
 #include "BsMonoClass.h"
+#include "BsMonoMethod.h"
 #include "BsSelection.h"
 #include "BsSelection.h"
 #include "BsScriptSceneObject.h"
 #include "BsScriptSceneObject.h"
 #include "BsMonoUtil.h"
 #include "BsMonoUtil.h"
@@ -8,6 +9,15 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	ScriptSelection::OnSelectionChangedThunkDef ScriptSelection::OnSelectionChangedThunk;
+	HEvent ScriptSelection::OnSelectionChangedConn;
+
+	ScriptSelection::ScriptSelection(MonoObject* instance)
+		:ScriptObject(instance)
+	{
+		
+	}
+
 	void ScriptSelection::initRuntimeData()
 	void ScriptSelection::initRuntimeData()
 	{
 	{
 		metaData.scriptClass->addInternalCall("Internal_GetSceneObjectSelection", &ScriptSelection::internal_GetSceneObjectSelection);
 		metaData.scriptClass->addInternalCall("Internal_GetSceneObjectSelection", &ScriptSelection::internal_GetSceneObjectSelection);
@@ -16,6 +26,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetResourceUUIDSelection", &ScriptSelection::internal_SetResourceUUIDSelection);
 		metaData.scriptClass->addInternalCall("Internal_SetResourceUUIDSelection", &ScriptSelection::internal_SetResourceUUIDSelection);
 		metaData.scriptClass->addInternalCall("Internal_GetResourcePathSelection", &ScriptSelection::internal_GetResourcePathSelection);
 		metaData.scriptClass->addInternalCall("Internal_GetResourcePathSelection", &ScriptSelection::internal_GetResourcePathSelection);
 		metaData.scriptClass->addInternalCall("Internal_SetResourcePathSelection", &ScriptSelection::internal_SetResourcePathSelection);
 		metaData.scriptClass->addInternalCall("Internal_SetResourcePathSelection", &ScriptSelection::internal_SetResourcePathSelection);
+
+		OnSelectionChangedThunk = (OnSelectionChangedThunkDef)metaData.scriptClass->getMethod("Internal_TriggerSelectionChanged", 2)->getThunk();
 	}
 	}
 
 
 	void ScriptSelection::internal_GetSceneObjectSelection(MonoArray** selection)
 	void ScriptSelection::internal_GetSceneObjectSelection(MonoArray** selection)
@@ -126,4 +138,39 @@ namespace BansheeEngine
 
 
 		Selection::instance().setResourcePaths(paths);
 		Selection::instance().setResourcePaths(paths);
 	}
 	}
+
+
+	void ScriptSelection::startUp()
+	{
+		OnSelectionChangedConn = Selection::instance().onSelectionChanged.connect(&ScriptSelection::onSelectionChanged);
+	}
+
+	void ScriptSelection::shutDown()
+	{
+		OnSelectionChangedConn.disconnect();
+	}
+
+	void ScriptSelection::onSelectionChanged(const Vector<HSceneObject>& sceneObjects, const Vector<Path>& resPaths)
+	{
+		UINT32 numObjects = (UINT32)sceneObjects.size();
+		ScriptArray scriptObjects = ScriptArray::create<ScriptSceneObject>(numObjects);
+		for (UINT32 i = 0; i < numObjects; i++)
+		{
+			// TODO - This bit is commonly used, I should add a method in ScriptGameObjectManager
+			ScriptSceneObject* scriptSceneObject = ScriptGameObjectManager::instance().getScriptSceneObject(sceneObjects[i]);
+			if (scriptSceneObject == nullptr)
+				scriptSceneObject = ScriptGameObjectManager::instance().createScriptSceneObject(sceneObjects[i]);
+
+			scriptObjects.set(i, scriptSceneObject->getManagedInstance());
+		}
+
+		UINT32 numPaths = (UINT32)resPaths.size();
+		ScriptArray scriptPaths = ScriptArray::create<String>(numPaths);
+		for (UINT32 i = 0; i < numPaths; i++)
+			scriptObjects.set(i, resPaths[i].toString());
+
+		MonoArray* monoObjects = scriptObjects.getInternal();
+		MonoArray* monoPaths = scriptPaths.getInternal();
+		MonoUtil::invokeThunk(OnSelectionChangedThunk, monoObjects, monoPaths);
+	}
 }
 }

+ 6 - 9
SBansheeEngine/Include/BsScriptGameObject.h

@@ -14,8 +14,8 @@ namespace BansheeEngine
 		virtual HGameObject getNativeHandle() const = 0;
 		virtual HGameObject getNativeHandle() const = 0;
 		virtual void setNativeHandle(const HGameObject& gameObject) = 0;
 		virtual void setNativeHandle(const HGameObject& gameObject) = 0;
 
 
-		virtual ScriptObjectBackup beginRefresh();
-		virtual void endRefresh(const ScriptObjectBackup& backupData);
+		virtual ScriptObjectBackup beginRefresh() override;
+		virtual void endRefresh(const ScriptObjectBackup& backupData) override;
 
 
 	protected:
 	protected:
 		bool mRefreshInProgress;
 		bool mRefreshInProgress;
@@ -24,14 +24,11 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ScriptGameObject : public ScriptObject<ScriptGameObject, ScriptGameObjectBase>
 	class BS_SCR_BE_EXPORT ScriptGameObject : public ScriptObject<ScriptGameObject, ScriptGameObjectBase>
 	{
 	{
 	public:
 	public:
-		static String getAssemblyName() { return ENGINE_ASSEMBLY; }
-		static String getNamespace() { return "BansheeEngine"; }
-		static String getTypeName() { return "GameObject"; }
-		static void initRuntimeData() { }
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "GameObject")
 
 
 	private:
 	private:
-		ScriptGameObject(MonoObject* instance)
-			:ScriptObject(instance)
-		{ }
+		ScriptGameObject(MonoObject* instance);
+
+		static UINT64 internal_getInstanceId(ScriptGameObject* nativeInstance);
 	};
 	};
 }
 }

+ 3 - 3
SBansheeEngine/Include/BsScriptSceneObject.h

@@ -14,8 +14,8 @@ namespace BansheeEngine
 
 
 		static bool checkIfDestroyed(ScriptSceneObject* nativeInstance);
 		static bool checkIfDestroyed(ScriptSceneObject* nativeInstance);
 
 
-		virtual HGameObject getNativeHandle() const { return mSceneObject; }
-		virtual void setNativeHandle(const HGameObject& gameObject);
+		virtual HGameObject getNativeHandle() const override { return mSceneObject; }
+		virtual void setNativeHandle(const HGameObject& gameObject) override;
 
 
 		HSceneObject getNativeSceneObject() const { return mSceneObject; }
 		HSceneObject getNativeSceneObject() const { return mSceneObject; }
 
 
@@ -66,7 +66,7 @@ namespace BansheeEngine
 		
 		
 		ScriptSceneObject(MonoObject* instance, const HSceneObject& sceneObject);
 		ScriptSceneObject(MonoObject* instance, const HSceneObject& sceneObject);
 
 
-		void _onManagedInstanceDeleted();
+		void _onManagedInstanceDeleted() override;
 
 
 		HSceneObject mSceneObject;
 		HSceneObject mSceneObject;
 	};
 	};

+ 15 - 0
SBansheeEngine/Source/BsScriptGameObject.cpp

@@ -1,4 +1,5 @@
 #include "BsScriptGameObject.h"
 #include "BsScriptGameObject.h"
+#include "BsDebug.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -19,4 +20,18 @@ namespace BansheeEngine
 
 
 		PersistentScriptObjectBase::endRefresh(backupData);
 		PersistentScriptObjectBase::endRefresh(backupData);
 	}
 	}
+
+	ScriptGameObject::ScriptGameObject(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptGameObject::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_GetInstanceId", &ScriptGameObject::internal_getInstanceId);
+	}
+
+	UINT64 ScriptGameObject::internal_getInstanceId(ScriptGameObject* nativeInstance)
+	{
+		return nativeInstance->getNativeHandle().getInstanceId();
+	}
 }
 }

+ 41 - 25
TODO.txt

@@ -11,7 +11,6 @@ Modal windows are set up as persistent but I don't serialize their internal data
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 C# Material/Shader:
 C# Material/Shader:
 
 
-TODO - Material/Shader has no color type so I cannot know when to display normal vector and when color in inspector
 TODO - Implement param block and sampler support
 TODO - Implement param block and sampler support
 TODO - When creating a Material without a shader, a default one should be used, at least in editor
 TODO - When creating a Material without a shader, a default one should be used, at least in editor
 TODO - Setting Material array parameters isn't possible from C#
 TODO - Setting Material array parameters isn't possible from C#
@@ -58,9 +57,14 @@ Code quality improvements:
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Polish stage 1
 Polish stage 1
 
 
+Test inspector selection, selecting a resource and adding/removing component updates
+Showing the inspector causes a considerable slowdown (maybe stuff gets refreshed too often?)
+Crash when showing the inspector (invalid index in Layout.InsertElement)
+
 Handle seems to lag behind the selected mesh
 Handle seems to lag behind the selected mesh
 ProjectLibrary seems to import some files on every start-up
 ProjectLibrary seems to import some files on every start-up
 Crash on shutdown in mono_gchandle_free
 Crash on shutdown in mono_gchandle_free
+Material/Shader has no color type so I cannot know when to display normal vector and when color in inspector
 
 
 First screenshot work:
 First screenshot work:
 - Inspector change contents on selection (and make sure the selected object/component looks okay)
 - Inspector change contents on selection (and make sure the selected object/component looks okay)
@@ -80,10 +84,12 @@ First screenshot work:
 SceneTreeView
 SceneTreeView
  - Hook up ping effect so it triggers when I select a resource or sceneobject
  - Hook up ping effect so it triggers when I select a resource or sceneobject
  - See if it needs other enhancements (rename, delete all work properly? etc.)
  - See if it needs other enhancements (rename, delete all work properly? etc.)
+ - Add copy/cut/paste/duplicate (with context menu)
 
 
 Finish up inspector
 Finish up inspector
  - Do I handle the case of updating the inspector when component is added or removed?
  - Do I handle the case of updating the inspector when component is added or removed?
  - Hook it up to Selection changes so it shows inspector for current component/resource
  - Hook it up to Selection changes so it shows inspector for current component/resource
+ - GUI TextureField similar to ResourceField but it displays the texture it has assigned
 
 
 Need a way to add scene objects and components (and remove them)
 Need a way to add scene objects and components (and remove them)
  - Components adding should be only done by drag and dropping scripts to inspector (undoable)
  - Components adding should be only done by drag and dropping scripts to inspector (undoable)
@@ -92,10 +98,12 @@ Need a way to add scene objects and components (and remove them)
  - Deleting them should be doable by context menu in Hierarchy and Del keystroke (undoable)
  - Deleting them should be doable by context menu in Hierarchy and Del keystroke (undoable)
 
 
 Add shortcut keys for view/move/rotate/scale
 Add shortcut keys for view/move/rotate/scale
-Add "focus on object" key (F)
+Add "focus on object" key (F) - animate it: rotate camera towards then speed towards while zooming in
 Ortographic camera views (+ gizmo in scene view corner that shows camera orientation)
 Ortographic camera views (+ gizmo in scene view corner that shows camera orientation)
 Drag to select in scene view
 Drag to select in scene view
 
 
+Replace "minimize" button in tabbed title bar with maximize and make sure it works
+
 Will need a status bar:
 Will need a status bar:
  - Displays last error message
  - Displays last error message
  - Opens up console on click
  - Opens up console on click
@@ -112,12 +120,14 @@ Later:
  - Undo/Redo when breaking or reverting a scene object
  - Undo/Redo when breaking or reverting a scene object
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
-Project window
-
-Later:
- - Might need to improve search (need to test). Do multiple search keywords work properly?
- - Consider delaying search until user stops pressing keys (so not to have thousands of search results in the initial stages)
- - Save & restore scroll position when Refresh happens
+Simple stuff
+ - Inject an icon into an .exe (Win32 specific)
+ - C# wrapper for GUISkin (and a way to assign the current skin to a window)
+ - Move all the code files into subfolders so their hierarchy is similar to VS filters
+ - Font doesn't have a C# interface
+ - Get rid of PoolAlloc and other unused allocators (plus fix bs_new and others which have weird overloads)
+ - Get rid of event callback from HString and figure out a better way
+ - Splash screen
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Resources
 Resources
@@ -138,27 +148,16 @@ Resources
    - Copy all the resources marked with the flag mentioned above to \Data subfolder in the output folder, preserving the same asset structure
    - Copy all the resources marked with the flag mentioned above to \Data subfolder in the output folder, preserving the same asset structure
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
-Simple stuff
+Project window
 
 
- - Inject an icon into an .exe (Win32 specific)
- - C# wrapper for GUISkin (and a way to assign the current skin to a window)
- - Move all the code files into subfolders so their hierarchy is similar to VS filters
- - Font doesn't have a C# interface
- - Get rid of PoolAlloc and other unused allocators (plus fix bs_new and others which have weird overloads)
- - Get rid of event callback from HString and figure out a better way
- - GUI TextureField similar to ResourceField but it displays the texture it has assigned
- - Splash screen
+Later:
+ - Might need to improve search (need to test). Do multiple search keywords work properly?
+ - Consider delaying search until user stops pressing keys (so not to have thousands of search results in the initial stages)
+ - Save & restore scroll position when Refresh happens
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Handles
 Handles
 
 
-When scaling using center make sure to offset the object before scale
-
-Rotate handle:
- - How to handle local/global with rotate handle?
-   - This maybe just determines initial rotation of the handle?
-   - I don't think my code properly handles rotation handle transforms (e.g. arc drawing)
-
 Ideally free scale handle indicator should always render and be interactable and never be hidden by axis scale indicators (Not high priority)
 Ideally free scale handle indicator should always render and be interactable and never be hidden by axis scale indicators (Not high priority)
 
 
 Later:
 Later:
@@ -168,7 +167,6 @@ Later:
 Include files:
 Include files:
 
 
 Test:
 Test:
- - Try preprocessing using one RenderAPi and then load the shaders using another and see if they're created normally
  - Test if default values work
  - Test if default values work
  - Test project library dependant resources (e.g. changing an include and seeing if shader is reimported)
  - Test project library dependant resources (e.g. changing an include and seeing if shader is reimported)
 
 
@@ -180,6 +178,24 @@ Test:
  - Handle snapping
  - Handle snapping
  - Multi-select Move/Rotate/scale
  - Multi-select Move/Rotate/scale
 
 
+----------------------------------------------------------------------
+Undo/Redo C#
+
+UndoRedo class
+ - RegisterCommand(cmd, operationName)
+ - pushGroup
+ - popGroup
+ - undo
+ - redo
+ - clear
+
+RecordObjectCommand
+ - Saves the entire object and at the end of the frame performs a diff of the object to actually generate the undo operation
+ - Must work for scene objects, components and resources
+SetParentCommand - For reparenting scene objects
+AddComponentCommand
+DestroyCommand - for destroying scene objects or components
+
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Other
 Other