Просмотр исходного кода

Support for multiple object selection

marco.bellan 9 лет назад
Родитель
Сommit
38466a310c

+ 21 - 8
Source/MBansheeEditor/Windows/Scene/SceneSelection.cs

@@ -53,24 +53,34 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="pointerPos">Position of the pointer relative to the scene camera viewport.</param>
         /// <param name="controlHeld">Should this selection add to the existing selection, or replace it.</param>
-        /// <param name="ignoreRenderables">An array of renderables that should not be rendered during scene picking.</param>
-        internal void PickObject(Vector2I pointerPos, bool controlHeld, SceneObject[] ignoreRenderables = null)
+        /// <param name="ignoreSceneObjects">An array of renderables that should not be rendered during scene picking.</param>
+        internal void PickObject(Vector2I pointerPos, bool controlHeld, SceneObject[] ignoreSceneObjects = null)
         {
-            Internal_PickObject(mCachedPtr, ref pointerPos, controlHeld, ignoreRenderables);
+            Internal_PickObject(mCachedPtr, ref pointerPos, controlHeld, ignoreSceneObjects);
+        }
+
+        /// <summary>
+        /// Attempts to select a scene object in the specified area.
+        /// </summary>
+        /// <param name="pointerPos">Position of the pointer relative to the scene camera viewport.</param>
+        /// <param name="area">The screen area in which objects will be selected.</param>
+        /// <param name="controlHeld">Should this selection add to the existing selection, or replace it.</param>
+        /// <param name="ignoreSceneObjects">An array of renderables that should not be rendered during scene picking.</param>
+        internal void PickObjects(Vector2I pointerPos, Vector2I area, bool controlHeld, SceneObject[] ignoreSceneObjects = null)
+        {
+            Internal_PickObjects(mCachedPtr, ref pointerPos, ref area, controlHeld, ignoreSceneObjects);
         }
 
         /// <summary>
         /// Returns the 3D position of an object under the cursor, along with the surface normal in that point.
         /// </summary>
         /// <param name="pointerPos">Position of the pointer relative to the scene camera viewport.</param>
-        /// <param name="ignoreRenderables">An array of renderables that should not be rendered during scene picking.</param>
+        /// <param name="ignoreSceneObjects">An array of renderables that should not be rendered during scene picking.</param>
         /// <returns>The position on the object surface and the normal in that point.</returns>
-        internal SnapData Snap(Vector2I pointerPos, SceneObject[] ignoreRenderables = null)
+        internal SnapData Snap(Vector2I pointerPos, SceneObject[] ignoreSceneObjects = null)
         {
             SnapData data;
-            if (ignoreRenderables == null)
-                ignoreRenderables = new SceneObject[0];
-            Internal_Snap(mCachedPtr, ref pointerPos, out data, ignoreRenderables);
+            Internal_Snap(mCachedPtr, ref pointerPos, out data, ignoreSceneObjects);
             return data;
         }
 
@@ -83,6 +93,9 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_PickObject(IntPtr thisPtr, ref Vector2I pointerPos, bool controlHeld, SceneObject[] ignoreRenderables);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_PickObjects(IntPtr thisPtr, ref Vector2I pointerPos, ref Vector2I extents, bool controlHeld, SceneObject[] ignoreRenderables);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Snap(IntPtr thisPtr, ref Vector2I pointerPos, out SnapData data, SceneObject[] ignoreRenderables);
     }

+ 92 - 9
Source/MBansheeEditor/Windows/Scene/SceneWindow.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Runtime.Remoting.Messaging;
 using BansheeEngine;
 
 namespace BansheeEditor
@@ -85,7 +86,10 @@ namespace BansheeEditor
         private bool dragActive;
         private SceneObject draggedSO;
         private Vector3 draggedSOOffset;
-        private Vector2I dragBegin;
+        private GUITexture dragSelection;
+        private bool isDraggingSelection;
+        private Vector2I dragSelectionStart;
+        private Vector2I dragSelectionEnd;
 
         /// <summary>
         /// Returns the scene camera.
@@ -447,6 +451,7 @@ namespace BansheeEditor
 
         private void OnEditorUpdate()
         {
+
             if (HasFocus)
             {
                 if (!Input.IsPointerButtonHeld(PointerButton.Right))
@@ -502,6 +507,22 @@ namespace BansheeEditor
             bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
             draggedOver &= IsPointerHovering && inBounds && DragDrop.Type == DragDropType.Resource;
 
+            if (inBounds)
+            {
+                if (Input.IsPointerButtonDown(PointerButton.Left))
+                {
+                    StartDragSelection(scenePos);
+                }
+                else if (Input.IsPointerButtonHeld(PointerButton.Left))
+                {
+                    UpdateDragSelection(scenePos);
+                }
+                else if (Input.IsPointerButtonUp(PointerButton.Left))
+                {
+                    EndDragSelection();
+                }
+            }
+
             if (draggedOver)
             {
                 if (DragDrop.DropInProgress)
@@ -513,12 +534,7 @@ namespace BansheeEditor
                         Selection.SceneObject = draggedSO;
                         EditorApplication.SetSceneDirty();
                     }
-                    else
-                    {
-                        //Selection.SceneObjects = sceneSelection.PickObjects(dragBegin, scenePos - dragBegin,
-                        //    Input.IsButtonHeld(ButtonCode.LeftControl));
-                    }
-
+                    
                     draggedSO = null;
                 }
                 else
@@ -526,7 +542,6 @@ namespace BansheeEditor
                     if (!dragActive)
                     {
                         dragActive = true;
-                        dragBegin = scenePos;
 
                         ResourceDragDropData dragData = (ResourceDragDropData)DragDrop.Data;
 
@@ -629,7 +644,7 @@ namespace BansheeEditor
                     }
                     else if (Input.IsPointerButtonUp(PointerButton.Left))
                     {
-                        if (!handleActive)
+                        if (!handleActive && !dragActive)
                         {
                             bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
                                             Input.IsButtonHeld(ButtonCode.RightControl);
@@ -928,6 +943,74 @@ namespace BansheeEditor
 
 		    objects = cleanList.ToArray();
 	    }
+
+        /// <summary>
+        /// Starts a drag operation that displays a selection outline allowing the user to select multiple entries at once.
+        /// </summary>
+        /// <param name="scenePos">Coordinates relative to the scene where the drag originated.</param>
+        private void StartDragSelection(Vector2I scenePos)
+        {
+            isDraggingSelection = true;
+            dragSelectionStart = scenePos;
+            dragSelectionEnd = dragSelectionStart;
+        }
+
+        /// <summary>
+        /// Updates a selection outline drag operation by expanding the outline to the new location. Elements in the outline
+        /// are selected.
+        /// </summary>
+        /// <param name="scenePos">Coordinates of the pointer relative to the scene.</param>
+        /// <returns>True if the selection outline drag is valid and was updated, false otherwise.</returns>
+        private bool UpdateDragSelection(Vector2I scenePos)
+        {
+            if (!isDraggingSelection)
+                return false;
+
+            if (dragSelection == null)
+            {
+                dragSelection = new GUITexture(null, true, EditorStyles.SelectionArea);
+                rtPanel.AddElement(dragSelection);
+            }
+
+            
+            dragSelectionEnd = scenePos;
+
+            Rect2I selectionArea = new Rect2I();
+
+            Vector2I min = new Vector2I(Math.Min(dragSelectionStart.x, dragSelectionEnd.x), Math.Min(dragSelectionStart.y, dragSelectionEnd.y));
+            Vector2I max = new Vector2I(Math.Max(dragSelectionStart.x, dragSelectionEnd.x), Math.Max(dragSelectionStart.y, dragSelectionEnd.y));
+            selectionArea.x = min.x;
+            selectionArea.y = min.y;
+            selectionArea.width = max.x - min.x;
+            selectionArea.height = max.y - min.y;
+
+            dragSelection.Bounds = selectionArea;
+
+            return true;
+        }
+
+        /// <summary>
+        /// Ends the selection outline drag operation. Elements in the outline are selected.
+        /// </summary>
+        /// <returns>True if the selection outline drag is valid and was ended, false otherwise.</returns>
+        private bool EndDragSelection()
+        {
+            if (!isDraggingSelection)
+                return false;
+
+            if (dragSelection != null)
+            {
+                dragSelection.Destroy();
+                dragSelection = null;
+            }
+            Debug.Log("heya");
+            Vector2I min = new Vector2I(Math.Min(dragSelectionStart.x, dragSelectionEnd.x), Math.Min(dragSelectionStart.y, dragSelectionEnd.y));
+            Vector2I max = new Vector2I(Math.Max(dragSelectionStart.x, dragSelectionEnd.x), Math.Max(dragSelectionStart.y, dragSelectionEnd.y));
+            sceneSelection.PickObjects(min, max - min, Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl));
+
+            isDraggingSelection = false;
+            return false;
+        }
     }
 
     /** @} */

+ 1 - 0
Source/SBansheeEditor/Include/BsScriptSceneSelection.h

@@ -31,6 +31,7 @@ namespace BansheeEngine
 		static void internal_Create(MonoObject* managedInstance, ScriptCamera* camera);
 		static void internal_Draw(ScriptSceneSelection* thisPtr);
 		static void internal_PickObject(ScriptSceneSelection* thisPtr, Vector2I* inputPos, bool additive, MonoArray* ignoreRenderables);
+		static void internal_PickObjects(ScriptSceneSelection* thisPtr, Vector2I* inputPos, Vector2I* area, bool additive, MonoArray* ignoreRenderables);
 		static void internal_Snap(ScriptSceneSelection* thisPtr, Vector2I* inputPos, SnapData* data, MonoArray* ignoreRenderables);
 	};
 

+ 62 - 5
Source/SBansheeEditor/Source/BsScriptSceneSelection.cpp

@@ -26,6 +26,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_Create", &ScriptSceneSelection::internal_Create);
 		metaData.scriptClass->addInternalCall("Internal_Draw", &ScriptSceneSelection::internal_Draw);
 		metaData.scriptClass->addInternalCall("Internal_PickObject", &ScriptSceneSelection::internal_PickObject);
+		metaData.scriptClass->addInternalCall("Internal_PickObjects", &ScriptSceneSelection::internal_PickObjects);
 		metaData.scriptClass->addInternalCall("Internal_Snap", &ScriptSceneSelection::internal_Snap);
 	}
 
@@ -41,7 +42,7 @@ namespace BansheeEngine
 
 	void ScriptSceneSelection::internal_PickObject(ScriptSceneSelection* thisPtr, Vector2I* inputPos, bool additive, MonoArray* ignoreRenderables)
 	{
-		// TODO - Handle multi-selection (i.e. selection rectangle when dragging)
+		assert(_CrtCheckMemory() == 1);
 		SnapData data;
 
 		Vector<HSceneObject> ignoredSceneObjects;
@@ -64,8 +65,8 @@ namespace BansheeEngine
 			}
 		}
 
-		HSceneObject pickedObject = ScenePicking::instance().pickClosestObject(thisPtr->mCamera, *inputPos, Vector2I(1, 1), data, ignoredSceneObjects);
-
+		HSceneObject pickedObject = ScenePicking::instance().pickClosestObject(thisPtr->mCamera, *inputPos, Vector2I(1, 1), ignoredSceneObjects);
+		assert(_CrtCheckMemory() == 1);
 		if (pickedObject)
 		{
 			if (additive) // Append to existing selection
@@ -88,8 +89,64 @@ namespace BansheeEngine
 				Selection::instance().setSceneObjects(selectedSOs);
 			}
 		}
-		else
+		else //TODO: Should we clear when it is additive?
+			Selection::instance().clearSceneSelection();
+		assert(_CrtCheckMemory() == 1);
+	}
+
+	void ScriptSceneSelection::internal_PickObjects(ScriptSceneSelection* thisPtr, Vector2I* inputPos, Vector2I* area, bool additive, MonoArray* ignoreRenderables)
+	{
+		SnapData data;
+		assert(_CrtCheckMemory() == 1);
+		Vector<HSceneObject> ignoredSceneObjects;
+
+		if (ignoreRenderables != nullptr)
+		{
+			ScriptArray scriptArray(ignoreRenderables);
+
+			UINT32 arrayLen = scriptArray.size();
+			for (UINT32 i = 0; i < arrayLen; i++)
+			{
+				MonoObject* monoSO = scriptArray.get<MonoObject*>(i);
+				ScriptSceneObject* scriptSO = ScriptSceneObject::toNative(monoSO);
+
+				if (scriptSO == nullptr)
+					continue;
+
+				HSceneObject so = static_object_cast<SceneObject>(scriptSO->getNativeHandle());
+				ignoredSceneObjects.push_back(so);
+			}
+		}
+		assert(_CrtCheckMemory() == 1);
+		Vector<HSceneObject> pickedObjects = ScenePicking::instance().pickObjects(thisPtr->mCamera, *inputPos, *area, ignoredSceneObjects);
+		assert(_CrtCheckMemory() == 1);
+		if (pickedObjects.size() != 0)
+		{
+			if (additive) // Append to existing selection
+			{
+				Vector<HSceneObject> selectedSOs = Selection::instance().getSceneObjects();
+
+				for (int i = 0; i < pickedObjects.size(); i++) {
+					bool found = false;
+					for (int j = 0; j < selectedSOs.size(); j++)
+					{
+						if (selectedSOs[j] == pickedObjects[i])
+						{
+							found = true;
+							break;
+						}
+					}
+					if (!found)
+						selectedSOs.push_back(pickedObjects[i]);
+				}
+				Selection::instance().setSceneObjects(selectedSOs);
+			}
+			else
+				Selection::instance().setSceneObjects(pickedObjects);
+		}
+		else //TODO: Should we clear when it is additive?
 			Selection::instance().clearSceneSelection();
+		assert(_CrtCheckMemory() == 1);
 	}
 
 	void ScriptSceneSelection::internal_Snap(ScriptSceneSelection* thisPtr, Vector2I* inputPos, SnapData* data, MonoArray* ignoreRenderables)
@@ -114,7 +171,7 @@ namespace BansheeEngine
 			}
 		}
 
-  		ScenePicking::instance().pickClosestObject(thisPtr->mCamera, *inputPos, Vector2I(1, 1), *data, ignoredSceneObjects);
+  		ScenePicking::instance().pickClosestObject(thisPtr->mCamera, *inputPos, Vector2I(1, 1), ignoredSceneObjects, data);
 	}
 
 }