Browse Source

WIP Multiple Selection

Josh Engebretson 10 years ago
parent
commit
5227160bc6

+ 1 - 3
Script/AtomicEditor/editor/Editor.ts

@@ -129,9 +129,7 @@ class Editor extends Atomic.ScriptObject {
 
     handleProjectUnloaded(event) {
 
-        this.sendEvent(EditorEvents.ActiveSceneChange, { scene: null });
-
-
+        this.sendEvent(EditorEvents.ActiveSceneEditorChange, { sceneEditor: null });
 
     }
 

+ 8 - 1
Script/AtomicEditor/editor/EditorEvents.ts

@@ -23,7 +23,14 @@ export interface PlayerLogEvent {
 }
 
 
-export const ActiveSceneChange = "EditorActiveSceneChange";
+export const ActiveSceneEditorChange = "EditorActiveSceneEditorChange";
+export interface ActiveSceneEditorChangeEvent {
+
+  sceneEditor: Editor.SceneEditor3D;
+
+}
+
+
 export const ActiveNodeChange = "EditorActiveNodeChange";
 export const SceneClosed = "EditorSceneClosed";
 export interface SceneClosedEvent {

+ 155 - 163
Script/AtomicEditor/ui/frames/HierarchyFrame.ts

@@ -15,6 +15,7 @@ var IconTemporary = "ComponentBitmap";
 class HierarchyFrame extends Atomic.UIWidget {
 
     scene: Atomic.Scene = null;
+    sceneEditor: Editor.SceneEditor3D;
     hierList: Atomic.UIListView;
     menu: HierarchyFrameMenu;
     nodeIDToItemID = {};
@@ -35,21 +36,15 @@ class HierarchyFrame extends Atomic.UIWidget {
         hierarchycontainer = this.getWidget("hierarchycontainer");
 
         var hierList = this.hierList = new Atomic.UIListView();
-
         hierList.rootList.id = "hierList_";
 
+        hierList.subscribeToEvent("WidgetEvent", (event: Atomic.UIWidgetEvent) => this.handleHierListWidgetEvent(event));
+
         hierarchycontainer.addChild(hierList);
 
         this.subscribeToEvent(this, "WidgetEvent", (data) => this.handleWidgetEvent(data));
 
-        this.subscribeToEvent(EditorEvents.ActiveNodeChange, (data) => {
-
-            if (data.node)
-                this.hierList.selectItemByID(data.node.id.toString());
-
-        });
-
-        this.subscribeToEvent(EditorEvents.ActiveSceneChange, (data) => this.handleActiveSceneChanged(data));
+        this.subscribeToEvent(EditorEvents.ActiveSceneEditorChange, (data) => this.handleActiveSceneEditorChanged(data));
 
         // handle dropping on hierarchy, moving node, dropping prefabs, etc
         this.subscribeToEvent(this.hierList.rootList, "DragEnded", (data) => this.handleDragEnded(data));
@@ -157,30 +152,27 @@ class HierarchyFrame extends Atomic.UIWidget {
 
         this.hierList.deleteItemByID(node.id.toString());
 
-        var selectedId = Number(this.hierList.rootList.selectedItemID);
-        var selectedNode = this.scene.getNode(selectedId);
-        if (selectedNode == node) {
-
-            this.sendEvent(EditorEvents.ActiveNodeChange, { node: ev.parent ? ev.parent : this.scene });
-
-        }
-
     }
 
-    handleActiveSceneChanged(data) {
+    handleActiveSceneEditorChanged(event: EditorEvents.ActiveSceneEditorChangeEvent) {
 
         if (this.scene)
             this.unsubscribeFromEvents(this.scene);
 
-        // clear selected node
-        this.sendEvent(EditorEvents.ActiveNodeChange, { node: null });
+        this.sceneEditor = null;
+        this.scene = null;
+
+        if (!event.sceneEditor)
+            return;
 
-        this.scene = <Atomic.Scene>data.scene;
+        this.sceneEditor = event.sceneEditor;
+        this.scene = event.sceneEditor.scene;
 
         this.populate();
 
         if (this.scene) {
 
+            this.subscribeToEvent(this.scene, "SceneNodeSelected", (event: Editor.SceneNodeSelectedEvent) => this.handleSceneNodeSelected(event));
             this.subscribeToEvent(this.scene, "NodeAdded", (ev: Atomic.NodeAddedEvent) => this.handleNodeAdded(ev));
             this.subscribeToEvent(this.scene, "NodeRemoved", (ev: Atomic.NodeRemovedEvent) => this.handleNodeRemoved(ev));
             this.subscribeToEvent(this.scene, "NodeNameChanged", (ev: Atomic.NodeNameChangedEvent) => {
@@ -193,147 +185,6 @@ class HierarchyFrame extends Atomic.UIWidget {
 
     }
 
-    handleWidgetEvent(data: Atomic.UIWidgetEvent): boolean {
-
-        if (data.type == Atomic.UI_EVENT_TYPE_KEY_UP) {
-
-            if (data.key == Atomic.KEY_DOWN || data.key == Atomic.KEY_UP || data.key == Atomic.KEY_LEFT || data.key == Atomic.KEY_RIGHT) {
-                var selectedId = Number(this.hierList.selectedItemID);
-                var node = this.scene.getNode(selectedId);
-
-                if (node) {
-
-                    this.sendEvent("EditorActiveNodeChange", { node: node });
-
-                }
-            }
-            if (data.key == Atomic.KEY_RIGHT) {
-                var selectedId = Number(this.hierList.selectedItemID);
-                var itemNodeId = this.nodeIDToItemID[selectedId];
-
-                if (!this.hierList.getExpanded(itemNodeId) && this.hierList.getExpandable(itemNodeId)) {
-                    this.hierList.setExpanded(itemNodeId, true);
-                    this.hierList.rootList.invalidateList();
-                } else {
-                    this.hierList.rootList.selectNextItem();
-                }
-
-            } else if (data.key == Atomic.KEY_LEFT) {
-                var selectedId = Number(this.hierList.selectedItemID);
-                var itemNodeId = this.nodeIDToItemID[selectedId];
-
-                if (this.hierList.getExpanded(itemNodeId)) {
-                    this.hierList.setExpanded(itemNodeId, false);
-                    this.hierList.rootList.invalidateList();
-                } else {
-                    var node = this.scene.getNode(selectedId);
-                    var parentNode = node.getParent();
-                    if (parentNode) {
-                        this.hierList.selectItemByID(parentNode.id.toString());
-                    }
-                }
-
-            }
-
-            // node deletion
-            if (data.key == Atomic.KEY_DELETE || data.key == Atomic.KEY_BACKSPACE) {
-
-                var selectedId = Number(this.hierList.rootList.selectedItemID);
-
-                var node = this.scene.getNode(selectedId);
-                if (node) {
-                    this.scene.sendEvent("SceneEditNodeAddedRemoved", { scene:this.scene, node:node, added:false});
-                    node.remove();
-
-                }
-
-            }
-
-        } else if (data.type == Atomic.UI_EVENT_TYPE_POINTER_DOWN) {
-
-            if (data.target == this.hierList.rootList) {
-
-                var node = this.scene.getNode(Number(data.refid));
-
-                if (node) {
-
-                    // set the widget's drag object
-                    var dragObject = new Atomic.UIDragObject(node, node.name.length ? "Node: " + node.name : "Node: (Anonymous)");
-                    this.hierList.rootList.dragObject = dragObject;
-
-                }
-
-            }
-
-        } else if (data.type == Atomic.UI_EVENT_TYPE_CLICK) {
-
-            if (this.menu.handleNodeContextMenu(data.target, data.refid)) {
-                return true;
-            }
-
-            var id = data.target.id;
-
-            if (id == "create popup") {
-
-                var selectedId = Number(this.hierList.rootList.selectedItemID);
-                var node = this.scene.getNode(selectedId);
-                if (this.menu.handlePopupMenu(data.target, data.refid, node))
-                    return true;
-
-            }
-
-            // create
-            if (id == "menu create") {
-                if (!ToolCore.toolSystem.project) return;
-                var src = MenuItemSources.getMenuItemSource("hierarchy create items");
-                var menu = new Atomic.UIMenuWindow(data.target, "create popup");
-                menu.show(src);
-                return true;
-
-            }
-
-
-            if (id == "hierList_") {
-
-                var list = <Atomic.UISelectList>data.target;
-
-                var selectedId = Number(list.selectedItemID);
-                var node = this.scene.getNode(selectedId);
-
-                if (node) {
-
-                    this.sendEvent("EditorActiveNodeChange", { node: node });
-
-                }
-
-                return false;
-
-            }
-        } else if (data.type == Atomic.UI_EVENT_TYPE_RIGHT_POINTER_UP) {
-
-            var id = data.target.id;
-            var db = ToolCore.getAssetDatabase();
-            var node: Atomic.Node;
-
-            if (id == "hierList_")
-                node = this.scene.getNode(Number(this.hierList.hoverItemID));
-            else
-                node = this.scene.getNode(Number(id));
-
-            if (node) {
-
-                this.menu.createNodeContextMenu(this, node, data.x, data.y);
-
-            }
-
-
-
-
-        }
-
-        return false;
-    }
-
     filterNode(node: Atomic.Node): boolean {
 
         if (!node) return false;
@@ -397,7 +248,7 @@ class HierarchyFrame extends Atomic.UIWidget {
 
         }
 
-        this.hierList.rootList.value = 0;
+        this.hierList.rootList.value = -1;
         this.hierList.setExpanded(parentID, true);
 
     }
@@ -448,6 +299,147 @@ class HierarchyFrame extends Atomic.UIWidget {
 
     }
 
+    handleSceneNodeSelected(ev: Editor.SceneNodeSelectedEvent) {
+
+        if (ev.selected) {
+            this.hierList.selectItemByID(ev.node.id.toString(), true, true);
+        } else {
+            this.hierList.selectItemByID(ev.node.id.toString(), false, true);
+        }
+
+        this.hierList.scrollToSelectedItem();
+
+    }
+
+    handleHierListWidgetEvent(event: Atomic.UIWidgetEvent): boolean {
+
+        if (event.type == Atomic.UI_EVENT_TYPE_CUSTOM) {
+
+            var hierList = this.hierList;
+            if (event.refid == "select_list_selection_changed") {
+
+                for (var i = 0; i < hierList.rootList.numItems; i++) {
+
+                    var selected = hierList.rootList.getItemSelected(i);
+                    var id = hierList.rootList.getItemID(i);
+                    var node = this.scene.getNode(Number(id));
+                    if (node) {
+
+                        if (selected)
+                            this.sceneEditor.selection.addNode(node);
+                        else
+                            this.sceneEditor.selection.removeNode(node);
+                    }
+                }
+            }
+        }
+
+        return false;
+
+    }
+
+    handleWidgetEvent(data: Atomic.UIWidgetEvent): boolean {
+
+        if (data.type == Atomic.UI_EVENT_TYPE_KEY_UP) {
+
+            if (data.key == Atomic.KEY_RIGHT) {
+                var selectedId = Number(this.hierList.selectedItemID);
+                var itemNodeId = this.nodeIDToItemID[selectedId];
+
+                if (!this.hierList.getExpanded(itemNodeId) && this.hierList.getExpandable(itemNodeId)) {
+                    this.hierList.setExpanded(itemNodeId, true);
+                    this.hierList.rootList.invalidateList();
+                } else {
+                    this.hierList.rootList.selectNextItem();
+                }
+
+            } else if (data.key == Atomic.KEY_LEFT) {
+                var selectedId = Number(this.hierList.selectedItemID);
+                var itemNodeId = this.nodeIDToItemID[selectedId];
+
+                if (this.hierList.getExpanded(itemNodeId)) {
+                    this.hierList.setExpanded(itemNodeId, false);
+                    this.hierList.rootList.invalidateList();
+                } else {
+                    var node = this.scene.getNode(selectedId);
+                    var parentNode = node.getParent();
+                    if (parentNode) {
+                        this.hierList.selectItemByID(parentNode.id.toString());
+                    }
+                }
+
+            }
+
+            // node deletion
+            if (data.key == Atomic.KEY_DELETE || data.key == Atomic.KEY_BACKSPACE) {
+                this.sceneEditor.selection.delete();
+            }
+
+        } else if (data.type == Atomic.UI_EVENT_TYPE_POINTER_DOWN) {
+
+            if (data.target == this.hierList.rootList) {
+
+                var node = this.scene.getNode(Number(data.refid));
+
+                if (node) {
+
+                    // set the widget's drag object
+                    var dragObject = new Atomic.UIDragObject(node, node.name.length ? "Node: " + node.name : "Node: (Anonymous)");
+                    this.hierList.rootList.dragObject = dragObject;
+
+                }
+
+            }
+
+        } else if (data.type == Atomic.UI_EVENT_TYPE_CLICK) {
+
+            if (this.menu.handleNodeContextMenu(data.target, data.refid)) {
+                return true;
+            }
+
+            var id = data.target.id;
+
+            if (id == "create popup") {
+
+                var selectedId = Number(this.hierList.rootList.selectedItemID);
+                var node = this.scene.getNode(selectedId);
+                if (this.menu.handlePopupMenu(data.target, data.refid, node))
+                    return true;
+
+            }
+
+            // create
+            if (id == "menu create") {
+                if (!ToolCore.toolSystem.project) return;
+                var src = MenuItemSources.getMenuItemSource("hierarchy create items");
+                var menu = new Atomic.UIMenuWindow(data.target, "create popup");
+                menu.show(src);
+                return true;
+
+            }
+
+
+        } else if (data.type == Atomic.UI_EVENT_TYPE_RIGHT_POINTER_UP) {
+
+            var id = data.target.id;
+            var node: Atomic.Node;
+
+            if (id == "hierList_")
+                node = this.scene.getNode(Number(this.hierList.hoverItemID));
+            else
+                node = this.scene.getNode(Number(id));
+
+            if (node) {
+
+                this.menu.createNodeContextMenu(this, node, data.x, data.y);
+
+            }
+
+        }
+
+        return false;
+    }
+
 }
 
 export = HierarchyFrame;

+ 2 - 3
Script/AtomicEditor/ui/frames/ResourceFrame.ts

@@ -76,8 +76,7 @@ class ResourceFrame extends ScriptWidget {
 
             var sceneEditor3D = new Editor.SceneEditor3D(path, this.tabcontainer);
             editor = sceneEditor3D;
-
-            this.sendEvent(EditorEvents.ActiveSceneChange, { scene: sceneEditor3D.scene });
+            this.sendEvent(EditorEvents.ActiveSceneEditorChange, { sceneEditor: sceneEditor3D });
 
         }
 
@@ -193,7 +192,7 @@ class ResourceFrame extends ScriptWidget {
 
                     if (w.editor.typeName == "SceneEditor3D") {
 
-                        this.sendEvent(EditorEvents.ActiveSceneChange, { scene: (<Editor.SceneEditor3D> w.editor).scene });
+                        this.sendEvent(EditorEvents.ActiveSceneEditorChange, { sceneEditor: (<Editor.SceneEditor3D> w.editor) });
 
                     }
 

+ 47 - 23
Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts

@@ -20,6 +20,8 @@ import PrefabInspector = require("./PrefabInspector");
 class InspectorFrame extends ScriptWidget {
 
     nodeInspector: NodeInspector;
+    scene: Atomic.Scene = null;
+    sceneEditor: Editor.SceneEditor3D;
 
     constructor() {
 
@@ -32,13 +34,49 @@ class InspectorFrame extends ScriptWidget {
         var container = this.getWidget("inspectorcontainer");
 
         this.subscribeToEvent(EditorEvents.EditResource, (data) => this.handleEditResource(data));
-        this.subscribeToEvent(EditorEvents.ActiveNodeChange, (data) => this.handleActiveNodeChange(data));
         this.subscribeToEvent("ProjectUnloaded", (data) => this.handleProjectUnloaded(data));
         this.subscribeToEvent("NodeRemoved", (ev: Atomic.NodeRemovedEvent) => this.handleNodeRemoved(ev));
 
+        this.subscribeToEvent(EditorEvents.ActiveSceneEditorChange, (data) => this.handleActiveSceneEditorChanged(data));
 
     }
 
+    handleActiveSceneEditorChanged(event: EditorEvents.ActiveSceneEditorChangeEvent) {
+
+        if (this.scene)
+            this.unsubscribeFromEvents(this.scene);
+
+        this.sceneEditor = null;
+        this.scene = null;
+
+        if (!event.sceneEditor)
+            return;
+
+        this.sceneEditor = event.sceneEditor;
+        this.scene = event.sceneEditor.scene;
+
+        if (this.scene) {
+
+            this.subscribeToEvent(this.scene, "SceneNodeSelected", (event: Editor.SceneNodeSelectedEvent) => this.handleSceneNodeSelected(event));
+
+        }
+
+    }
+
+    handleSceneNodeSelected(ev: Editor.SceneNodeSelectedEvent) {
+
+        var selection = this.sceneEditor.selection;
+
+        if (selection.selectedNodeCount == 1) {
+            this.inspectNode(selection.getSelectedNode(0));
+        } else {
+            this.closeNodeInspector();
+        }
+
+        return;
+    }
+
+
     handleProjectUnloaded(data) {
 
         this.closeNodeInspector();
@@ -62,35 +100,21 @@ class InspectorFrame extends ScriptWidget {
 
     }
 
-    handleActiveNodeChange(data) {
-
-        var node = <Atomic.Node> data.node;
-
-        if (!node) {
-
-            this.closeNodeInspector();
-            return;
-        }
-
-        this.inspectNode(node);
-
-    }
-
     closeNodeInspector() {
 
-      if (this.nodeInspector) {
-          this.nodeInspector.saveState();
-          var container = this.getWidget("inspectorcontainer");
-          container.deleteAllChildren();
-          this.nodeInspector = null;
-      }
+        if (this.nodeInspector) {
+            this.nodeInspector.saveState();
+            var container = this.getWidget("inspectorcontainer");
+            container.deleteAllChildren();
+            this.nodeInspector = null;
+        }
 
     }
 
 
     inspectAsset(asset: ToolCore.Asset) {
 
-        this.sendEvent(EditorEvents.ActiveNodeChange, {node:null});
+        this.sendEvent(EditorEvents.ActiveNodeChange, { node: null });
 
         var container = this.getWidget("inspectorcontainer");
         container.deleteAllChildren();
@@ -108,7 +132,7 @@ class InspectorFrame extends ScriptWidget {
 
             var cache = Atomic.getResourceCache();
 
-            var material = <Atomic.Material> cache.getResource("Material", asset.path);
+            var material = <Atomic.Material>cache.getResource("Material", asset.path);
 
             if (!material) {
                 return;

+ 3 - 1
Script/Packages/Editor/Editor.json

@@ -1,7 +1,9 @@
 {
 	"name" : "Editor",
+	"includes" : ["<Atomic/Graphics/DebugRenderer.h>"],
 	"sources" : ["Source/AtomicEditor/Application", "Source/AtomicEditor/Utils",
 							"Source/AtomicEditor/EditorMode", "Source/AtomicEditor/PlayerMode",
 							 "Source/AtomicEditor/Editors", "Source/AtomicEditor/Editors/SceneEditor3D"],
-	"classes" : ["EditorMode", "PlayerMode", "FileUtils", "ResourceEditor", "JSResourceEditor", "SceneEditor3D", "SceneView3D"]
+	"classes" : ["EditorMode", "PlayerMode", "FileUtils", "ResourceEditor", "JSResourceEditor",
+								"SceneEditor3D", "SceneView3D", "SceneSelection"]
 }

+ 7 - 0
Script/TypeScript/AtomicWork.d.ts

@@ -249,6 +249,13 @@ declare module AtomicNET {
 
 declare module Editor {
 
+  export interface SceneNodeSelectedEvent {
+    scene: Atomic.Scene;
+    node: Atomic.Node;
+    selected: boolean;
+  }
+
+
   export interface GizmoEditModeChangedEvent {
     mode:EditMode;
   }

+ 6 - 4
Source/Atomic/UI/UIListView.cpp

@@ -543,7 +543,7 @@ void UIListView::DeleteAllItems()
 }
 
 
-void UIListView::SelectItemByID(const String& id)
+void UIListView::SelectItemByID(const String& id, bool selected, bool focus)
 {
     TBID tid = TBIDC(id.CString());
 
@@ -553,9 +553,11 @@ void UIListView::SelectItemByID(const String& id)
 
         if (tid == item->id)
         {
-            //item->SetExpanded(true);
-            rootList_->SetValue(i);
-            rootList_->InvalidateList();
+            if (selected && focus)
+                rootList_->SetValue(i);
+            else
+                rootList_->SelectItem(i, selected);
+
             return;
         }
 

+ 1 - 1
Source/Atomic/UI/UIListView.h

@@ -59,7 +59,7 @@ public:
     bool GetExpandable(unsigned itemID);
 
     void DeleteAllItems();
-    void SelectItemByID(const String& id);
+    void SelectItemByID(const String& id, bool selected = true, bool focus = true);
 
     String GetHoverItemID() { return rootList_.Null() ? "" : rootList_->GetHoverItemID(); }
     String GetSelectedItemID() { return rootList_.Null() ? "" : rootList_->GetSelectedItemID(); }

+ 41 - 0
Source/Atomic/UI/UISelectList.cpp

@@ -65,6 +65,25 @@ void UISelectList::SetFilter(const String& filter)
     ((TBSelectList*)widget_)->SetFilter(filter.CString());
 }
 
+int UISelectList::GetNumItems() const
+{
+    if (!widget_)
+        return 0;
+
+    return ((TBSelectList*)widget_)->GetNumItems();
+
+}
+
+void UISelectList::SelectItem(int index, bool selected)
+{
+    if (!widget_)
+        return;
+
+    ((TBSelectList*)widget_)->SelectItem(index, selected);
+
+}
+
+
 void UISelectList::SetValue(int value)
 {
     if (!widget_)
@@ -103,6 +122,28 @@ String UISelectList::GetSelectedItemID()
     GetSubsystem<UI>()->GetTBIDString(id, id_);
 
     return id_;
+}
+
+bool UISelectList::GetItemSelected(int index)
+{
+    if (!widget_)
+        return false;
+
+    return ((TBSelectList*)widget_)->GetItemSelected(index);
+}
+
+String UISelectList::GetItemID(int index)
+{
+    if (!widget_)
+        return "";
+
+    String _id;
+
+    TBID id = ((TBSelectList*)widget_)->GetItemID(index);
+
+    GetSubsystem<UI>()->GetTBIDString(id, _id);
+
+    return _id;
 
 }
 

+ 8 - 0
Source/Atomic/UI/UISelectList.h

@@ -51,10 +51,18 @@ public:
     double GetValue();
 
     String GetHoverItemID();
+
     String GetSelectedItemID();
 
     void ScrollToSelectedItem();
 
+    String GetItemID(int index);
+    bool GetItemSelected(int index);
+
+    int GetNumItems() const;
+
+    void SelectItem(int index, bool selected = true);
+
     tb::TBSelectList* GetTBSelectList();
 
     void SelectNextItem();

+ 26 - 1
Source/Atomic/UI/UIWidget.cpp

@@ -165,6 +165,15 @@ void UIWidget::ConvertEvent(UIWidget *handler, UIWidget* target, const tb::TBWid
         }
     }
 
+    unsigned modifierKeys = 0;
+
+    if( ev.special_key && TB_SHIFT)
+        modifierKeys |= QUAL_SHIFT;
+    if( ev.special_key && TB_CTRL)
+        modifierKeys |= QUAL_CTRL;
+    if( ev.special_key && TB_ALT)
+        modifierKeys |= QUAL_ALT;
+
     using namespace WidgetEvent;
     data[P_HANDLER] = handler;
     data[P_TARGET] = target;
@@ -175,8 +184,9 @@ void UIWidget::ConvertEvent(UIWidget *handler, UIWidget* target, const tb::TBWid
     data[P_DELTAY] = ev.delta_y;
     data[P_COUNT] = ev.count;
     data[P_KEY] = key;
+
     data[P_SPECIALKEY] = (unsigned) ev.special_key;
-    data[P_MODIFIERKEYS] = (unsigned) ev.modifierkeys;
+    data[P_MODIFIERKEYS] = modifierKeys;
     data[P_REFID] = refid;
     data[P_TOUCH] = (unsigned) ev.touch;
 }
@@ -747,6 +757,21 @@ bool UIWidget::OnEvent(const tb::TBWidgetEvent &ev)
         }
 
     }
+    if (ev.type == EVENT_TYPE_CUSTOM)
+    {
+        if (!ev.target || ui->IsWidgetWrapped(ev.target))
+        {
+            VariantMap eventData;
+            ConvertEvent(this, ui->WrapWidget(ev.target), ev, eventData);
+            SendEvent(E_WIDGETEVENT, eventData);
+
+            if (eventData[WidgetEvent::P_HANDLED].GetBool())
+                return true;
+
+        }
+
+    }
+
 
     return false;
 }

+ 3 - 3
Source/AtomicEditor/EditorMode/AEEditorEvents.h

@@ -94,17 +94,17 @@ EVENT(E_EDITORMODAL, EditorModal)
     PARAM(P_MESSAGE, Message);    // for modal errors, error text
 }
 
-EVENT(E_EDITORACTIVESCENECHANGE, EditorActiveSceneChange)
+EVENT(E_EDITORACTIVESCENEEDITORCHANGE, EditorActiveSceneEditorChange)
 {
-    PARAM(P_SCENE, Scene);      // Scene*
+    PARAM(P_SCENEEDITOR, SceneEditor);
 }
 
+
 EVENT(E_EDITORACTIVENODECHANGE, EditorActiveNodeChange)
 {
     PARAM(P_NODE, Node);      // Node*
 }
 
-
 EVENT(E_PLAYERERROR, PlayerError)
 {
     PARAM(P_TEXT, Text);      // string

+ 30 - 22
Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.cpp

@@ -15,6 +15,8 @@
 #include <Atomic/Input/Input.h>
 
 #include "SceneEditor3DEvents.h"
+#include "SceneSelection.h"
+#include "SceneEditor3D.h"
 #include "Gizmo3D.h"
 
 namespace AtomicEditor
@@ -59,6 +61,7 @@ void Gizmo3D::SetView(SceneView3D* view3D)
     view3D_ = view3D;
     scene_ = view3D->GetScene();
     camera_ = view3D->GetCameraNode()->GetComponent<Camera>();
+    selection_ = view3D_->GetSceneEditor3D()->GetSelection();
     assert(camera_.NotNull());
 }
 
@@ -67,30 +70,32 @@ void Gizmo3D::Position()
     Vector3 center(0, 0, 0);
     bool containsScene = false;
 
-    for (unsigned i = 0; i < editNodes_->Size(); ++i)
+    Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
+
+    for (unsigned i = 0; i < editNodes.Size(); ++i)
     {
         // Scene's transform should not be edited, so hide gizmo if it is included
-        if (editNodes_->At(i) == scene_)
+        if (editNodes[i] == scene_)
         {
             containsScene = true;
             break;
         }
-        center += editNodes_->At(i)->GetWorldPosition();
+        center += editNodes[i]->GetWorldPosition();
     }
 
-    if (editNodes_->Empty() || containsScene)
+    if (editNodes.Empty() || containsScene)
     {
         Hide();
         return;
     }
 
-    center /= editNodes_->Size();
+    center /= editNodes.Size();
     gizmoNode_->SetPosition(center);
 
-    if (axisMode_ == AXIS_WORLD || editNodes_->Size() > 1)
+    if (axisMode_ == AXIS_WORLD || editNodes.Size() > 1)
         gizmoNode_->SetRotation(Quaternion());
     else
-        gizmoNode_->SetRotation(editNodes_->At(0)->GetWorldRotation());
+        gizmoNode_->SetRotation(editNodes[0]->GetWorldRotation());
 
     ResourceCache* cache = GetSubsystem<ResourceCache>();
 
@@ -137,10 +142,8 @@ void Gizmo3D::Position()
     }
 }
 
-void Gizmo3D::Update(Vector<Node *> &editNodes)
+void Gizmo3D::Update()
 {
-    editNodes_ = &editNodes;
-
     Use();
     Position();
 }
@@ -156,14 +159,13 @@ void Gizmo3D::Use()
 {
     if (gizmo_.Null() || !gizmo_->IsEnabled() || editMode_ == EDIT_SELECT)
     {
-        //StoreGizmoEditActions();
-        //previousGizmoDrag = false;
         return;
     }
 
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     Input* input = GetSubsystem<Input>();
 
+    Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
     Ray cameraRay = view3D_->GetCameraRay();
     float scale = gizmoNode_->GetScale().x_;
 
@@ -174,7 +176,7 @@ void Gizmo3D::Use()
         if (dragging_)
         {
             VariantMap eventData;
-            eventData[SceneEditSerializable::P_SERIALIZABLE] = editNodes_->At(0);
+            eventData[SceneEditSerializable::P_SERIALIZABLE] = editNodes[0];
             eventData[SceneEditSerializable::P_OPERATION] = 1;
             scene_->SendEvent(E_SCENEEDITSERIALIZABLE, eventData);
             dragging_ = false;
@@ -187,7 +189,7 @@ void Gizmo3D::Use()
     gizmoAxisY_.Update(cameraRay, scale, drag, camera_->GetNode());
     gizmoAxisZ_.Update(cameraRay, scale, drag, camera_->GetNode());
 
-    if (!editNodes_->Size() || editNodes_->At(0) == scene_)
+    if (!editNodes.Size() || editNodes[0] == scene_)
     {
         gizmoAxisX_.selected_ = gizmoAxisY_.selected_ = gizmoAxisZ_.selected_ = false;
         // this just forces an update
@@ -238,9 +240,11 @@ bool Gizmo3D::MoveEditNodes(Vector3 adjust)
     bool moveSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
 #endif
 
+    Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
+
     if (adjust.Length() > M_EPSILON)
     {
-        for (unsigned i = 0; i < editNodes_->Size(); ++i)
+        for (unsigned i = 0; i < editNodes.Size(); ++i)
         {
             if (moveSnap)
             {
@@ -252,9 +256,9 @@ bool Gizmo3D::MoveEditNodes(Vector3 adjust)
                 adjust.z_ = floorf(adjust.z_ / moveStepScaled + 0.5) * moveStepScaled;
             }
 
-            Node* node = editNodes_->At(i);
+            Node* node = editNodes[i];
             Vector3 nodeAdjust = adjust;
-            if (axisMode_ == AXIS_LOCAL && editNodes_->Size() == 1)
+            if (axisMode_ == AXIS_LOCAL && editNodes.Size() == 1)
                 nodeAdjust = node->GetWorldRotation() * nodeAdjust;
 
             Vector3 worldPos = node->GetWorldPosition();
@@ -289,6 +293,8 @@ bool Gizmo3D::RotateEditNodes(Vector3 adjust)
     bool rotateSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
 #endif
 
+    Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
+
     if (rotateSnap)
     {
         float rotateStepScaled = snapRotation_;
@@ -301,11 +307,12 @@ bool Gizmo3D::RotateEditNodes(Vector3 adjust)
     {
         moved = true;
 
-        for (unsigned i = 0; i < editNodes_->Size(); ++i)
+        for (unsigned i = 0; i < editNodes.Size(); ++i)
         {
-            Node* node = editNodes_->At(i);
+            Node* node = editNodes[i];
+
             Quaternion rotQuat(adjust.x_, adjust.y_, adjust.z_);
-            if (axisMode_ == AXIS_LOCAL && editNodes_->Size() == 1)
+            if (axisMode_ == AXIS_LOCAL && editNodes.Size() == 1)
                 node->SetRotation(node->GetRotation() * rotQuat);
             else
             {
@@ -336,12 +343,13 @@ bool Gizmo3D::ScaleEditNodes(Vector3 adjust)
     bool scaleSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
 #endif
 
+    Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
 
     if (adjust.Length() > M_EPSILON)
     {
-        for (unsigned i = 0; i < editNodes_->Size(); ++i)
+        for (unsigned i = 0; i < editNodes.Size(); ++i)
         {
-            Node* node = editNodes_->At(i);
+            Node* node = editNodes[i];
 
             Vector3 scale = node->GetScale();
             Vector3 oldScale = scale;

+ 5 - 2
Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.h

@@ -31,6 +31,8 @@ namespace Atomic
 namespace AtomicEditor
 {
 
+class SceneSelection;
+
 class Gizmo3DAxis
 {
 public:
@@ -121,7 +123,7 @@ public:
 
     void Show();
     void Hide();
-    void Update(Vector<Node*>& editNodes);
+    void Update();
 
     Node* GetGizmoNode() { return gizmoNode_; }
 
@@ -155,6 +157,7 @@ private:
     WeakPtr<Scene> scene_;
     WeakPtr<Camera> camera_;
     WeakPtr<StaticModel> gizmo_;
+    WeakPtr<SceneSelection> selection_;
 
     Gizmo3DAxis gizmoAxisX_;
     Gizmo3DAxis gizmoAxisY_;
@@ -165,9 +168,9 @@ private:
 
     AxisMode axisMode_;
 
-    Vector<Node *> *editNodes_;
     bool dragging_;
 
+    // snap settings
     float snapTranslationX_;
     float snapTranslationY_;
     float snapTranslationZ_;

+ 10 - 64
Source/AtomicEditor/Editors/SceneEditor3D/SceneEditor3D.cpp

@@ -35,6 +35,7 @@
 
 #include "SceneEditor3D.h"
 #include "SceneEditHistory.h"
+#include "SceneSelection.h"
 #include "SceneEditor3DEvents.h"
 
 using namespace ToolCore;
@@ -63,6 +64,7 @@ SceneEditor3D ::SceneEditor3D(Context* context, const String &fullpath, UITabCon
 
     scene_->SetUpdateEnabled(false);
 
+    selection_ = new SceneSelection(context, this);
     sceneView_ = new SceneView3D(context_, this);
 
     // EARLY ACCESS
@@ -130,17 +132,7 @@ bool SceneEditor3D::OnEvent(const TBWidgetEvent &ev)
     {
         if (ev.special_key == TB_KEY_DELETE || ev.special_key == TB_KEY_BACKSPACE)
         {
-            if (selectedNode_)
-            {
-                VariantMap editData;
-                editData[SceneEditNodeAddedRemoved::P_SCENE] = scene_;
-                editData[SceneEditNodeAddedRemoved::P_NODE] = selectedNode_;
-                editData[SceneEditNodeAddedRemoved::P_ADDED] = false;
-                scene_->SendEvent(E_SCENEEDITNODEADDEDREMOVED, editData);
-
-                selectedNode_->Remove();
-                selectedNode_ = 0;
-            }
+            selection_->Delete();
         }
 
     }
@@ -149,28 +141,11 @@ bool SceneEditor3D::OnEvent(const TBWidgetEvent &ev)
     {
         if (ev.ref_id == TBIDC("copy"))
         {
-            if (selectedNode_.NotNull())
-            {
-                clipboardNode_ = selectedNode_;
-            }
+            selection_->Copy();
         }
         else if (ev.ref_id == TBIDC("paste"))
         {
-            if (clipboardNode_.NotNull() && selectedNode_.NotNull())
-            {
-                SharedPtr<Node> pasteNode(clipboardNode_->Clone());
-
-                VariantMap eventData;
-                eventData[EditorActiveNodeChange::P_NODE] = pasteNode;
-                SendEvent(E_EDITORACTIVENODECHANGE, eventData);
-
-                VariantMap editData;
-                editData[SceneEditNodeAddedRemoved::P_SCENE] = scene_;
-                editData[SceneEditNodeAddedRemoved::P_NODE] = pasteNode;
-                editData[SceneEditNodeAddedRemoved::P_ADDED] = true;
-
-                scene_->SendEvent(E_SCENEEDITNODEADDEDREMOVED, editData);
-            }
+            selection_->Paste();
         }
         else if (ev.ref_id == TBIDC("close"))
         {
@@ -223,13 +198,13 @@ void SceneEditor3D::SetFocus()
 
 void SceneEditor3D::SelectNode(Node* node)
 {
+    /*
     selectedNode_ = node;
     if (!node)
         gizmo3D_->Hide();
     else
         gizmo3D_->Show();
-
-
+    */
 }
 
 void SceneEditor3D::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
@@ -244,16 +219,15 @@ void SceneEditor3D::HandleNodeAdded(StringHash eventType, VariantMap& eventData)
 void SceneEditor3D::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
 {
     Node* node = (Node*) (eventData[NodeRemoved::P_NODE].GetPtr());
+    /*
     if (node == selectedNode_)
         SelectNode(0);
+    */
 }
 
 void SceneEditor3D::HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
-    Vector<Node*> editNodes;
-    if (selectedNode_.NotNull())
-        editNodes.Push(selectedNode_);
-    gizmo3D_->Update(editNodes);
+    gizmo3D_->Update();
 }
 
 void SceneEditor3D::HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData)
@@ -341,34 +315,6 @@ void SceneEditor3D::HandleUserPrefSaved(StringHash eventType, VariantMap& eventD
     UpdateGizmoSnapSettings();
 }
 
-void SceneEditor3D::GetSelectionBoundingBox(BoundingBox& bbox)
-{
-    bbox.Clear();
-
-    if (selectedNode_.Null())
-        return;
-
-    // TODO: Adjust once multiple selection is in
-    if (selectedNode_.Null())
-        return;
-
-    // Get all the drawables, which define the bounding box of the selection
-    PODVector<Drawable*> drawables;
-    selectedNode_->GetDerivedComponents<Drawable>(drawables, true);
-
-    if (!drawables.Size())
-        return;
-
-    // Calculate the combined bounding box of all drawables
-    for (unsigned i = 0; i < drawables.Size(); i++  )
-    {
-        Drawable* drawable = drawables[i];
-        bbox.Merge(drawable->GetWorldBoundingBox());
-    }
-
-
-}
-
 void SceneEditor3D::UpdateGizmoSnapSettings()
 {
     gizmo3D_->SetSnapTranslationX(userPrefs_->GetSnapTranslationX());

+ 6 - 2
Source/AtomicEditor/Editors/SceneEditor3D/SceneEditor3D.h

@@ -38,6 +38,7 @@ namespace AtomicEditor
 {
 
 class SceneEditHistory;
+class SceneSelection;
 
 class SceneEditor3D: public ResourceEditor
 {
@@ -52,7 +53,9 @@ public:
     bool OnEvent(const TBWidgetEvent &ev);
 
     void SelectNode(Node* node);
-    void GetSelectionBoundingBox(BoundingBox& bbox);
+
+    SceneSelection* GetSelection() { return selection_; }
+    SceneView3D* GetSceneView3D() { return sceneView_; }
 
     Scene* GetScene() { return scene_; }
     Gizmo3D* GetGizmo() { return gizmo3D_; }
@@ -94,7 +97,8 @@ private:
 
     SharedPtr<Gizmo3D> gizmo3D_;
 
-    WeakPtr<Node> selectedNode_;
+    SharedPtr<SceneSelection> selection_;
+
     SharedPtr<Node> clipboardNode_;
 
     SharedPtr<SceneEditHistory> editHistory_;

+ 7 - 0
Source/AtomicEditor/Editors/SceneEditor3D/SceneEditor3DEvents.h

@@ -30,6 +30,13 @@ EVENT(E_GIZMOMOVED, GizmoMoved)
 
 }
 
+EVENT(E_SCENENODESELECTED, SceneNodeSelected)
+{
+    PARAM(P_SCENE, Scene);             // Scene
+    PARAM(P_NODE, Node);               // Node
+    PARAM(P_SELECTED, Selected);       // bool
+}
+
 EVENT(E_SCENEEDITSCENEMODIFIED, SceneEditSceneModified)
 {
 

+ 231 - 0
Source/AtomicEditor/Editors/SceneEditor3D/SceneSelection.cpp

@@ -0,0 +1,231 @@
+
+#include <Atomic/Core/CoreEvents.h>
+
+#include <Atomic/Graphics/Graphics.h>
+#include <Atomic/Graphics/Drawable.h>
+#include <Atomic/Graphics/DebugRenderer.h>
+#include <Atomic/Atomic3D/Terrain.h>
+
+#include <Atomic/Scene/SceneEvents.h>
+#include <Atomic/Scene/Node.h>
+#include <Atomic/Scene/Scene.h>
+
+#include "SceneEditor3D.h"
+#include "SceneEditor3DEvents.h"
+#include "SceneSelection.h"
+
+namespace AtomicEditor
+{
+
+SceneSelection::SceneSelection(Context* context, SceneEditor3D *sceneEditor) : Object(context)
+{
+    sceneEditor3D_ = sceneEditor;
+    scene_ = sceneEditor3D_->GetScene();
+
+    SubscribeToEvent(E_POSTRENDERUPDATE, HANDLER(SceneSelection, HandlePostRenderUpdate));
+    SubscribeToEvent(scene_, E_NODEREMOVED, HANDLER(SceneSelection, HandleNodeRemoved));
+}
+
+SceneSelection::~SceneSelection()
+{
+
+}
+
+Node* SceneSelection::GetSelectedNode(unsigned index) const
+{
+    if (index > nodes_.Size())
+        return 0;
+
+    return nodes_[index];
+}
+
+bool SceneSelection::Contains(Node* node)
+{
+    SharedPtr<Node> _node(node);
+    return nodes_.Contains(_node);
+}
+
+void SceneSelection::AddNode(Node* node, bool clear)
+{
+    if (clear)
+        Clear();
+
+    SharedPtr<Node> _node(node);
+    if (!nodes_.Contains(_node))
+    {
+        nodes_.Push(_node);
+
+        VariantMap eventData;
+        eventData[SceneNodeSelected::P_SCENE] = scene_;
+        eventData[SceneNodeSelected::P_NODE] = node;
+        eventData[SceneNodeSelected::P_SELECTED] = true;
+        scene_->SendEvent(E_SCENENODESELECTED, eventData);
+    }
+}
+
+void SceneSelection::RemoveNode(Node* node)
+{    
+    SharedPtr<Node> _node(node);
+    if(!nodes_.Contains(_node))
+        return;
+
+    nodes_.Remove(_node);
+
+    VariantMap eventData;
+    eventData[SceneNodeSelected::P_SCENE] = scene_;
+    eventData[SceneNodeSelected::P_NODE] = node;
+    eventData[SceneNodeSelected::P_SELECTED] = false;    
+    scene_->SendEvent(E_SCENENODESELECTED, eventData);
+
+
+
+}
+
+void SceneSelection::Clear()
+{
+    Vector<SharedPtr<Node>> nodes = nodes_;
+
+    for (unsigned i = 0; i < nodes.Size(); i++)
+    {
+        RemoveNode(nodes[i]);
+    }
+
+}
+
+void SceneSelection::Paste()
+{
+    /*
+    if (clipboardNode_.NotNull() && selectedNode_.NotNull())
+    {
+        SharedPtr<Node> pasteNode(clipboardNode_->Clone());
+
+        VariantMap eventData;
+        eventData[EditorActiveNodeChange::P_NODE] = pasteNode;
+        SendEvent(E_EDITORACTIVENODECHANGE, eventData);
+
+        VariantMap editData;
+        editData[SceneEditNodeAddedRemoved::P_SCENE] = scene_;
+        editData[SceneEditNodeAddedRemoved::P_NODE] = pasteNode;
+        editData[SceneEditNodeAddedRemoved::P_ADDED] = true;
+
+        scene_->SendEvent(E_SCENEEDITNODEADDEDREMOVED, editData);
+    }
+    */
+
+}
+
+
+void SceneSelection::Copy()
+{
+    /*
+
+    if (selectedNode_.NotNull())
+    {
+        clipboardNode_ = selectedNode_;
+    }
+
+    */
+}
+
+void SceneSelection::Delete()
+{
+
+    Vector<SharedPtr<Node>> nodes = nodes_;
+
+    Clear();
+
+    for (unsigned i = 0; i < nodes.Size(); i++)
+    {
+        nodes[i]->Remove();
+    }
+
+    /*
+    if (selectedNode_)
+    {
+        VariantMap editData;
+        editData[SceneEditNodeAddedRemoved::P_SCENE] = scene_;
+        editData[SceneEditNodeAddedRemoved::P_NODE] = selectedNode_;
+        editData[SceneEditNodeAddedRemoved::P_ADDED] = false;
+        scene_->SendEvent(E_SCENEEDITNODEADDEDREMOVED, editData);
+
+        selectedNode_->Remove();
+        selectedNode_ = 0;
+    }
+    */
+}
+
+void SceneSelection::GetBounds(BoundingBox& bbox)
+{
+
+    bbox.Clear();
+
+    if (!nodes_.Size())
+        return;
+
+    // Get all the drawables, which define the bounding box of the selection
+    PODVector<Drawable*> drawables;
+
+    for (unsigned i = 0; i < nodes_.Size(); i++)
+    {
+        Node* node = nodes_[i];
+        node->GetDerivedComponents<Drawable>(drawables, true, false);
+    }
+
+    if (!drawables.Size())
+        return;
+
+    // Calculate the combined bounding box of all drawables
+    for (unsigned i = 0; i < drawables.Size(); i++  )
+    {
+        Drawable* drawable = drawables[i];
+        bbox.Merge(drawable->GetWorldBoundingBox());
+    }
+
+}
+
+void SceneSelection::DrawNodeDebug(Node* node, DebugRenderer* debug, bool drawNode)
+{
+    if (drawNode)
+        debug->AddNode(node, 1.0, false);
+
+    // Exception for the scene to avoid bringing the editor to its knees: drawing either the whole hierarchy or the subsystem-
+    // components can have a large performance hit. Also do not draw terrain child nodes due to their large amount
+    // (TerrainPatch component itself draws nothing as debug geometry)
+    if (node != scene_ && !node->GetComponent<Terrain>())
+    {
+        const Vector<SharedPtr<Component> >& components = node->GetComponents();
+
+        for (unsigned j = 0; j < components.Size(); ++j)
+            components[j]->DrawDebugGeometry(debug, false);
+
+        // To avoid cluttering the view, do not draw the node axes for child nodes
+        for (unsigned k = 0; k < node->GetNumChildren(); ++k)
+            DrawNodeDebug(node->GetChild(k), debug, false);
+    }
+}
+
+void SceneSelection::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
+{
+    Node* node = (Node*) (eventData[NodeRemoved::P_NODE].GetPtr());
+    RemoveNode(node);
+}
+
+
+void SceneSelection::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
+{
+
+    if (!nodes_.Size())
+        return;
+
+    // Visualize the currently selected nodes
+    DebugRenderer* debugRenderer = sceneEditor3D_->GetSceneView3D()->GetDebugRenderer();
+
+    for (unsigned i = 0; i < nodes_.Size(); i++)
+    {
+        DrawNodeDebug(nodes_[i], debugRenderer);
+    }
+
+}
+
+
+}

+ 65 - 0
Source/AtomicEditor/Editors/SceneEditor3D/SceneSelection.h

@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace Atomic
+{
+class Node;
+class Scene;
+}
+
+namespace AtomicEditor
+{
+
+class SceneEditor3D;
+
+class SceneSelection: public Object
+{
+    OBJECT(SceneSelection);
+
+public:
+
+    SceneSelection(Context* context, SceneEditor3D* sceneEditor);
+    virtual ~SceneSelection();
+
+    Vector<SharedPtr<Node>>& GetNodes() { return nodes_; }
+
+    void Copy();
+    void Paste();
+    void Delete();
+
+    /// Add a node to the selection, if clear specified removes current nodes first
+    void AddNode(Node* node, bool clear = false);
+    void RemoveNode(Node* node);
+    void GetBounds(BoundingBox& bbox);
+
+    bool Contains(Node* node);
+
+    Node* GetSelectedNode(unsigned index) const;
+    unsigned GetSelectedNodeCount() const { return nodes_.Size(); }
+
+    void Clear();
+
+private:
+
+    void DrawNodeDebug(Node* node, DebugRenderer* debug, bool drawNode = true);
+
+    void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData);
+    void HandleNodeRemoved(StringHash eventType, VariantMap& eventData);
+
+    WeakPtr<SceneEditor3D> sceneEditor3D_;
+    WeakPtr<Scene> scene_;
+    Vector<SharedPtr<Node>> nodes_;
+
+};
+
+}

+ 17 - 38
Source/AtomicEditor/Editors/SceneEditor3D/SceneView3D.cpp

@@ -20,7 +20,6 @@
 #include <Atomic/Graphics/Octree.h>
 #include <Atomic/Graphics/Material.h>
 
-#include <Atomic/Atomic3D/Terrain.h>
 #include <Atomic/Atomic3D/Model.h>
 #include <Atomic/Atomic3D/StaticModel.h>
 #include <Atomic/Atomic3D/AnimatedModel.h>
@@ -45,6 +44,7 @@
 #include "SceneView3D.h"
 #include "SceneEditor3D.h"
 #include "SceneEditor3DEvents.h"
+#include "SceneSelection.h"
 
 using namespace ToolCore;
 
@@ -72,6 +72,7 @@ SceneView3D ::SceneView3D(Context* context, SceneEditor3D *sceneEditor) :
     if (debugRenderer_.Null())
     {
         debugRenderer_ = scene_->CreateComponent<DebugRenderer>();
+        debugRenderer_->SetTemporary(true);
     }
 
     octree_ = scene_->GetComponent<Octree>();
@@ -195,7 +196,7 @@ void SceneView3D::MoveCamera(float timeStep)
     if (orbitting)
     {
         BoundingBox bbox;
-        sceneEditor_->GetSelectionBoundingBox(bbox);
+        sceneEditor_->GetSelection()->GetBounds(bbox);
         if (bbox.defined_)
         {
             Vector3 centerPoint = bbox.Center();
@@ -299,27 +300,6 @@ Ray SceneView3D::GetCameraRay()
                                   float(cpos.y_ - y) / rect.Height());
 }
 
-void SceneView3D::DrawNodeDebug(Node* node, DebugRenderer* debug, bool drawNode)
-{
-    if (drawNode)
-        debug->AddNode(node, 1.0, false);
-
-    // Exception for the scene to avoid bringing the editor to its knees: drawing either the whole hierarchy or the subsystem-
-    // components can have a large performance hit. Also do not draw terrain child nodes due to their large amount
-    // (TerrainPatch component itself draws nothing as debug geometry)
-    if (node != scene_ && !node->GetComponent<Terrain>())
-    {
-        const Vector<SharedPtr<Component> >& components = node->GetComponents();
-
-        for (unsigned j = 0; j < components.Size(); ++j)
-            components[j]->DrawDebugGeometry(debug, false);
-
-        // To avoid cluttering the view, do not draw the node axes for child nodes
-        for (unsigned k = 0; k < node->GetNumChildren(); ++k)
-            DrawNodeDebug(node->GetChild(k), debug, false);
-    }
-}
-
 bool SceneView3D::MouseInView()
 {
     if (!GetInternalWidget())
@@ -367,19 +347,13 @@ void SceneView3D::HandleUIWidgetFocusEscaped(StringHash eventType, VariantMap& e
 void SceneView3D::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
 {
 
-    // Visualize the currently selected nodes
-    if (selectedNode_.NotNull())
-    {
-        DrawNodeDebug(selectedNode_, debugRenderer_);
-
-    }
-
     if (!MouseInView() || GetOrbitting())
         return;
 
     Input* input = GetSubsystem<Input>();
 
     mouseLeftDown_ = false;
+    bool shiftDown = input->GetKeyDown(KEY_LSHIFT) || input->GetKeyDown(KEY_RSHIFT);
 
     if (input->GetMouseButtonPress(MOUSEB_LEFT))
     {
@@ -409,8 +383,14 @@ void SceneView3D::HandlePostRenderUpdate(StringHash eventType, VariantMap& event
                     if (node->IsTemporary())
                         node = node->GetParent();
 
-                    neventData[EditorActiveNodeChange::P_NODE] = node;
-                    SendEvent(E_EDITORACTIVENODECHANGE, neventData);
+                    if (sceneEditor_->GetSelection()->Contains(node) && shiftDown)
+                    {
+                        sceneEditor_->GetSelection()->RemoveNode(node);
+                    }
+                    else
+                    {
+                        sceneEditor_->GetSelection()->AddNode(node, !shiftDown);
+                    }
 
                 }
             }
@@ -462,7 +442,7 @@ void SceneView3D::HandlePostRenderUpdate(StringHash eventType, VariantMap& event
 
 void SceneView3D::SelectNode(Node* node)
 {
-    selectedNode_ = node;
+    //selectedNode_ = node;
 }
 
 bool SceneView3D::OnEvent(const TBWidgetEvent &ev)
@@ -525,8 +505,8 @@ void SceneView3D::HandleEditorActiveNodeChange(StringHash eventType, VariantMap&
 void SceneView3D::HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
 {
     Node* node = (Node*) (eventData[NodeRemoved::P_NODE].GetPtr());
-    if (node == selectedNode_)
-        SelectNode(0);
+    //if (node == selectedNode_)
+    //    SelectNode(0);
 }
 
 void SceneView3D::UpdateDragNode(int mouseX, int mouseY)
@@ -676,8 +656,7 @@ void SceneView3D::HandleDragEnded(StringHash eventType, VariantMap& eventData)
 void SceneView3D::FrameSelection()
 {
     BoundingBox bbox;
-
-    sceneEditor_->GetSelectionBoundingBox(bbox);
+    sceneEditor_->GetSelection()->GetBounds(bbox);
 
     if (!bbox.defined_)
         return;
@@ -687,7 +666,7 @@ void SceneView3D::FrameSelection()
     if (sphere.radius_ < .01f || sphere.radius_ > 512)
         return;
 
-    framedNode_ = selectedNode_;
+    //framedNode_ = selectedNode_;
     cameraMoveStart_ = cameraNode_->GetWorldPosition();
     cameraMoveTarget_ = bbox.Center() - (cameraNode_->GetWorldDirection() * sphere.radius_ * 3);
     cameraMoveTime_ = 0.0f;

+ 5 - 4
Source/AtomicEditor/Editors/SceneEditor3D/SceneView3D.h

@@ -52,6 +52,9 @@ public:
     void Disable();
     bool IsEnabled() { return enabled_; }
 
+    DebugRenderer* GetDebugRenderer() { return debugRenderer_; }
+    SceneEditor3D* GetSceneEditor3D() { return sceneEditor_; }
+
 private:
 
     bool MouseInView();
@@ -72,9 +75,7 @@ private:
     void HandleNodeRemoved(StringHash eventType, VariantMap& eventData);
 
     void HandleUIWidgetFocusEscaped(StringHash eventType, VariantMap& eventData);
-    void HandleUIUnhandledShortcut(StringHash eventType, VariantMap& eventData);
-
-    void DrawNodeDebug(Node* node, DebugRenderer* debug, bool drawNode = true);
+    void HandleUIUnhandledShortcut(StringHash eventType, VariantMap& eventData);    
 
     void MoveCamera(float timeStep);
 
@@ -96,7 +97,7 @@ private:
     SharedPtr<Camera> camera_;
     SharedPtr<DebugRenderer> debugRenderer_;
     SharedPtr<Octree> octree_;
-    SharedPtr<Node> selectedNode_;
+
     WeakPtr<Node> framedNode_;
 
     SharedPtr<Scene> preloadResourceScene_;

+ 70 - 3
Source/ThirdParty/TurboBadger/tb_select.cpp

@@ -206,7 +206,6 @@ void TBSelectList::SetValue(int value)
 	if (value == m_value)
 		return;
 
-	SelectItem(m_value, false);
 	m_value = value;
 	SelectItem(m_value, true);
 	ScrollToSelectedItem();
@@ -217,6 +216,23 @@ void TBSelectList::SetValue(int value)
 	InvokeEvent(ev);
 }
 
+TBID TBSelectList::GetItemID(int index) const
+{
+    if (!m_source)
+        return TBID();
+
+    return m_source->GetItemID(index);
+
+}
+
+int TBSelectList::GetNumItems() const
+{
+    if (!m_source)
+        return 0;
+
+    return m_source->GetNumItems();
+}
+
 TBID TBSelectList::GetSelectedItemID()
 {
 	if (m_source && m_value >= 0 && m_value < m_source->GetNumItems())
@@ -227,7 +243,17 @@ TBID TBSelectList::GetSelectedItemID()
 void TBSelectList::SelectItem(int index, bool selected)
 {
 	if (TBWidget *widget = GetItemWidget(index))
+    {
+        bool changed = widget->GetState(WIDGET_STATE_SELECTED) != selected;
 		widget->SetState(WIDGET_STATE_SELECTED, selected);
+        if (changed)
+        {
+            TBWidgetEvent ev(EVENT_TYPE_CUSTOM);
+            ev.ref_id = TBIDC("select_list_selection_changed");
+            // forward to delegate
+            TBWidget::OnEvent(ev);
+        }
+    }
 }
 
 TBWidget *TBSelectList::GetItemWidget(int index)
@@ -279,7 +305,23 @@ bool TBSelectList::OnEvent(const TBWidgetEvent &ev)
 		TBWidgetSafePointer this_widget(this);
 
 		int index = ev.target->data.GetInt();
-		SetValue(index);
+
+        if (ev.modifierkeys & TB_SHIFT || ev.modifierkeys & TB_CTRL || ev.modifierkeys & TB_SUPER)
+        {
+            if (GetItemSelected(index))
+            {
+                SelectItem(index, false);
+            }
+            else
+            {
+               SetValue(index);
+            }
+        }
+        else
+        {
+            SelectAllItems(false);
+            SetValue(index);
+        }
 
 		// If we're still around, invoke the click event too.
 		if (this_widget.Get())
@@ -352,12 +394,37 @@ bool TBSelectList::ChangeValue(SPECIAL_KEY key)
 	// Select and focus what we found
 	if (current)
 	{
+        SelectAllItems(false);
 		SetValue(current->data.GetInt());
 		return true;
 	}
 	return false;
 }
 
+bool TBSelectList::GetItemSelected(int index)
+{
+    if (TBWidget *widget = GetItemWidget(index))
+        return widget->GetState(WIDGET_STATE_SELECTED);
+
+    return false;
+
+}
+
+void TBSelectList::SelectAllItems(bool select)
+{
+    if (!m_source)
+        return;
+
+    for (int i = 0; i < m_source->GetNumItems(); i++)
+    {
+        SelectItem(i, select);
+    }
+
+    if (!select)
+        m_value = -1;
+
+}
+
 // == TBSelectDropdown ==========================================
 
 TBSelectDropdown::TBSelectDropdown()
@@ -470,4 +537,4 @@ bool TBSelectDropdown::OnEvent(const TBWidgetEvent &ev)
 	return false;
 }
 
-}; // namespace tb
+} // namespace tb

+ 8 - 0
Source/ThirdParty/TurboBadger/tb_select.h

@@ -76,6 +76,7 @@ public:
 		to unselect the previously selected item, use SetValue. */
 	void SelectItem(int index, bool selected);
 	TBWidget *GetItemWidget(int index);
+    bool GetItemSelected(int index);
 
 	/** Scroll to the current selected item. The scroll may be delayed until
 		the items has been layouted if the layout is currently invalid. */
@@ -84,6 +85,9 @@ public:
 	/** Return the scrollcontainer used in this list. */
 	TBScrollContainer *GetScrollContainer() { return &m_container; }
 
+    TBID GetItemID(int index) const;
+    int GetNumItems() const;
+
 	virtual void OnInflate(const INFLATE_INFO &info);
 	virtual void OnSkinChanged();
 	virtual void OnProcess();
@@ -108,6 +112,10 @@ protected:
 private:
     TBSelectListSortCallback m_sort_callback;
 	TBWidget *CreateAndAddItemAfter(int index, TBWidget *reference);
+
+    void SelectAllItems(bool select = false);
+
+
 };
 
 /** TBSelectDropdown shows a button that opens a popup with a TBSelectList with items