Added CSceneIterator and implemented support for Select All/Invert Selection

This commit is contained in:
parax0 2016-01-09 09:39:43 -07:00
parent 7eeb90b925
commit 38d04bcd25
20 changed files with 459 additions and 48 deletions

View File

@ -180,7 +180,8 @@ HEADERS += \
Resource/Model/EVertexAttribute.h \
Render/FRenderOptions.h \
Scene/FShowFlags.h \
Scene/CScene.h
Scene/CScene.h \
Scene/CSceneIterator.h
# Source Files
SOURCES += \
@ -265,4 +266,5 @@ SOURCES += \
Resource/Script/IPropertyTemplate.cpp \
Resource/Script/IProperty.cpp \
Scene/FShowFlags.cpp \
Scene/CScene.cpp
Scene/CScene.cpp \
Scene/CSceneIterator.cpp

View File

@ -31,7 +31,7 @@ CModelNode* CScene::CreateModelNode(CModel *pModel)
if (pModel == nullptr) return nullptr;
CModelNode *pNode = new CModelNode(this, mpSceneRootNode, pModel);
mNodes[eShowObjects].push_back(pNode);
mNodes[eModelNode].push_back(pNode);
mNumNodes++;
return pNode;
}
@ -41,7 +41,7 @@ CStaticNode* CScene::CreateStaticNode(CStaticModel *pModel)
if (pModel == nullptr) return nullptr;
CStaticNode *pNode = new CStaticNode(this, mpAreaRootNode, pModel);
mNodes[eShowWorld].push_back(pNode);
mNodes[eStaticNode].push_back(pNode);
mNumNodes++;
return pNode;
}
@ -51,7 +51,7 @@ CCollisionNode* CScene::CreateCollisionNode(CCollisionMeshGroup *pMesh)
if (pMesh == nullptr) return nullptr;
CCollisionNode *pNode = new CCollisionNode(this, mpAreaRootNode, pMesh);
mNodes[eShowWorldCollision].push_back(pNode);
mNodes[eCollisionNode].push_back(pNode);
mNumNodes++;
return pNode;
}
@ -61,7 +61,7 @@ CScriptNode* CScene::CreateScriptNode(CScriptObject *pObj)
if (pObj == nullptr) return nullptr;
CScriptNode *pNode = new CScriptNode(this, mpAreaRootNode, pObj);
mNodes[eShowObjects].push_back(pNode);
mNodes[eScriptNode].push_back(pNode);
mNumNodes++;
return pNode;
}
@ -71,7 +71,7 @@ CLightNode* CScene::CreateLightNode(CLight *pLight)
if (pLight == nullptr) return nullptr;
CLightNode *pNode = new CLightNode(this, mpAreaRootNode, pLight);
mNodes[eShowLights].push_back(pNode);
mNodes[eLightNode].push_back(pNode);
mNumNodes++;
return pNode;
}
@ -115,7 +115,7 @@ void CScene::SetActiveArea(CGameArea *pArea)
{
CScriptLayer *pLayer = mpArea->GetScriptLayer(iLyr);
u32 NumObjects = pLayer->GetNumObjects();
mNodes[eShowObjects].reserve(mNodes[eShowObjects].size() + NumObjects);
mNodes[eScriptNode].reserve(mNodes[eScriptNode].size() + NumObjects);
for (u32 iObj = 0; iObj < NumObjects; iObj++)
{
@ -202,10 +202,11 @@ void CScene::AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
{
// Override show flags in game mode
FShowFlags ShowFlags = (ViewInfo.GameMode ? gkGameModeShowFlags : ViewInfo.ShowFlags);
FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags);
for (auto it = mNodes.begin(); it != mNodes.end(); it++)
{
if (ShowFlags & it->first)
if (NodeFlags & it->first)
{
std::vector<CSceneNode*>& rNodeVec = it->second;
@ -219,11 +220,12 @@ void CScene::AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
SRayIntersection CScene::SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo)
{
FShowFlags ShowFlags = (ViewInfo.GameMode ? gkGameModeShowFlags : ViewInfo.ShowFlags);
FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags);
CRayCollisionTester Tester(Ray);
for (auto it = mNodes.begin(); it != mNodes.end(); it++)
{
if (ShowFlags & it->first)
if (NodeFlags & it->first)
{
std::vector<CSceneNode*>& rNodeVec = it->second;
@ -252,7 +254,7 @@ CScriptNode* CScene::NodeForObject(CScriptObject *pObj)
CLightNode* CScene::NodeForLight(CLight *pLight)
{
// Slow. Is there a better way to do this?
std::vector<CSceneNode*>& rLights = mNodes[eShowLights];
std::vector<CSceneNode*>& rLights = mNodes[eLightNode];
for (auto it = rLights.begin(); it != rLights.end(); it++)
{
@ -291,3 +293,24 @@ CGameArea* CScene::GetActiveArea()
{
return mpArea;
}
// ************ STATIC ************
FShowFlags CScene::ShowFlagsForNodeFlags(FNodeFlags NodeFlags)
{
FShowFlags Out;
if (NodeFlags & eStaticNode) Out |= eShowWorld;
if (NodeFlags & eScriptNode) Out |= eShowObjects;
if (NodeFlags & eCollisionNode) Out |= eShowWorldCollision;
if (NodeFlags & eLightNode) Out |= eShowLights;
return Out;
}
FNodeFlags CScene::NodeFlagsForShowFlags(FShowFlags ShowFlags)
{
FNodeFlags Out = eRootNode | eModelNode;
if (ShowFlags & eShowWorld) Out |= eStaticNode;
if (ShowFlags & eShowWorldCollision) Out |= eCollisionNode;
if (ShowFlags & eShowObjects) Out |= eScriptNode | eScriptExtraNode;
if (ShowFlags & eShowLights) Out |= eLightNode;
return Out;
}

View File

@ -22,11 +22,13 @@
class CScene
{
friend class CSceneIterator;
bool mSplitTerrain;
u32 mNumNodes;
CRootNode *mpSceneRootNode;
std::unordered_map<EShowFlag, std::vector<CSceneNode*>> mNodes;
std::unordered_map<ENodeType, std::vector<CSceneNode*>> mNodes;
TResPtr<CGameArea> mpArea;
TResPtr<CWorld> mpWorld;
@ -60,6 +62,10 @@ public:
// Setters/Getters
CModel* GetActiveSkybox();
CGameArea* GetActiveArea();
// Static
static FShowFlags ShowFlagsForNodeFlags(FNodeFlags NodeFlags);
static FNodeFlags NodeFlagsForShowFlags(FShowFlags ShowFlags);
};
#endif // CSCENE_H

View File

@ -0,0 +1,72 @@
#include "CSceneIterator.h"
#include "CScene.h"
CSceneIterator::CSceneIterator(CScene *pScene, FNodeFlags AllowedNodeTypes /*= eAllNodeTypes*/, bool AllowHiddenNodes /*= false*/)
: mpScene(pScene)
, mAllowHiddenNodes(AllowHiddenNodes)
, mNodeFlags(AllowedNodeTypes)
, mpCurNode(nullptr)
{
mMapIterator = mpScene->mNodes.begin();
while (mMapIterator != mpScene->mNodes.end())
{
if (mMapIterator->first & AllowedNodeTypes)
break;
mMapIterator++;
}
if (mMapIterator != mpScene->mNodes.end())
{
mVectorIterator = (mMapIterator->second).begin();
Next(); // Find first node
}
}
// ************ PROTECTED ************
void CSceneIterator::InternalFindNext()
{
// This function does most of the heavy lifting. We continue from where we left off last time this function was called.
while (mMapIterator != mpScene->mNodes.end())
{
// Iterate over each node in the vector.
std::vector<CSceneNode*>& rVector = mMapIterator->second;
bool FoundNext = false;
while (mVectorIterator != rVector.end())
{
CSceneNode *pNode = *mVectorIterator;
// Check node visibility
if (mAllowHiddenNodes || pNode->IsVisible())
{
mpCurNode = pNode;
FoundNext = true;
}
mVectorIterator++;
if (FoundNext) return;
}
// We've reached the end of this node vector, so advance the map iterator
while (true)
{
mMapIterator++;
if (mMapIterator == mpScene->mNodes.end())
{
break;
}
if (mNodeFlags & mMapIterator->first)
{
mVectorIterator = mMapIterator->second.begin();
break;
}
}
}
// If we're down here, then it seems we're done iterating the scene.
mpCurNode = nullptr;
}

View File

@ -0,0 +1,66 @@
#ifndef CSCENEITERATOR_H
#define CSCENEITERATOR_H
#include "ENodeType.h"
#include <unordered_map>
class CScene;
class CSceneNode;
class CSceneIterator
{
CScene *mpScene;
bool mAllowHiddenNodes;
FNodeFlags mNodeFlags;
CSceneNode *mpCurNode;
std::unordered_map<ENodeType, std::vector<CSceneNode*>>::iterator mMapIterator;
std::vector<CSceneNode*>::iterator mVectorIterator;
public:
CSceneIterator(CScene *pScene, FNodeFlags AllowedNodeTypes = eAllNodeTypes, bool AllowHiddenNodes = false);
inline CSceneNode* Next()
{
InternalFindNext();
return mpCurNode;
}
inline bool DoneIterating() const
{
return (mpCurNode == nullptr);
}
inline CSceneNode* GetNode() const
{
return mpCurNode;
}
inline CSceneNode* operator*() const
{
return mpCurNode;
}
inline CSceneNode* operator->() const
{
return mpCurNode;
}
inline CSceneIterator& operator++()
{
Next();
return *this;
}
inline CSceneIterator operator++(int)
{
CSceneIterator Copy = *this;
Next();
return Copy;
}
protected:
void InternalFindNext();
};
#endif // CSCENEITERATOR_H

View File

@ -1,16 +1,21 @@
#ifndef ENODETYPE_H
#define ENODETYPE_H
#include <Common/Flags.h>
enum ENodeType
{
eRootNode,
eModelNode,
eStaticNode,
eCollisionNode,
eScriptNode,
eScriptExtraNode,
eLightNode
eRootNode = 0x0,
eModelNode = 0x1,
eStaticNode = 0x2,
eCollisionNode = 0x4,
eScriptNode = 0x8,
eScriptExtraNode = 0x10,
eLightNode = 0x20,
eAllNodeTypes = 0x3F
};
DECLARE_FLAGS(ENodeType, FNodeFlags)
#endif // ENODETYPE_H

View File

@ -45,6 +45,11 @@ void CSceneViewport::SetShowFlag(EShowFlag Flag, bool Visible)
mViewInfo.ShowFlags &= ~Flag;
}
FShowFlags CSceneViewport::ShowFlags() const
{
return mViewInfo.ShowFlags;
}
CRenderer* CSceneViewport::Renderer()
{
return mpRenderer;

View File

@ -32,6 +32,7 @@ public:
~CSceneViewport();
void SetScene(INodeEditor *pEditor, CScene *pScene);
void SetShowFlag(EShowFlag Flag, bool Visible);
FShowFlags ShowFlags() const;
CRenderer* Renderer();
CSceneNode* HoverNode();
CVector3f HoverPoint();

View File

@ -125,7 +125,9 @@ HEADERS += \
INodeEditor.h \
TestDialog.h \
UICommon.h \
CErrorLogDialog.h
CErrorLogDialog.h \
Undo/CSelectAllCommand.h \
Undo/CInvertSelectionCommand.h
# Source Files
SOURCES += \
@ -171,7 +173,9 @@ SOURCES += \
main.cpp \
TestDialog.cpp \
UICommon.cpp \
CErrorLogDialog.cpp
CErrorLogDialog.cpp \
Undo/CSelectAllCommand.cpp \
Undo/CInvertSelectionCommand.cpp
# UI Files
FORMS += \

View File

@ -2,12 +2,13 @@
#include "Editor/Undo/UndoCommands.h"
INodeEditor::INodeEditor(QWidget *pParent)
: QMainWindow(pParent),
mShowGizmo(false),
mGizmoHovering(false),
mGizmoTransforming(false),
mTranslateSpace(eWorldTransform),
mRotateSpace(eWorldTransform)
: QMainWindow(pParent)
, mSelectionLocked(false)
, mShowGizmo(false)
, mGizmoHovering(false)
, mGizmoTransforming(false)
, mTranslateSpace(eWorldTransform)
, mRotateSpace(eWorldTransform)
{
// Create undo actions
QAction *pUndoAction = mUndoStack.createUndoAction(this);
@ -133,39 +134,68 @@ void INodeEditor::ExpandSelectionBounds(CSceneNode *pNode)
void INodeEditor::SelectNode(CSceneNode *pNode)
{
if (!pNode->IsSelected())
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection));
if (!mSelectionLocked)
{
if (!pNode->IsSelected())
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection));
}
}
void INodeEditor::DeselectNode(CSceneNode *pNode)
{
if (pNode->IsSelected())
mUndoStack.push(new CDeselectNodeCommand(this, pNode, mSelection));
if (!mSelectionLocked)
{
if (pNode->IsSelected())
mUndoStack.push(new CDeselectNodeCommand(this, pNode, mSelection));
}
}
void INodeEditor::ClearSelection()
{
if (!mSelection.empty())
mUndoStack.push(new CClearSelectionCommand(this, mSelection));
if (!mSelectionLocked)
{
if (!mSelection.empty())
mUndoStack.push(new CClearSelectionCommand(this, mSelection));
}
}
void INodeEditor::ClearAndSelectNode(CSceneNode *pNode)
{
if (mSelection.empty())
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection));
else if ((mSelection.size() == 1) && (mSelection.front() == pNode))
return;
else
if (!mSelectionLocked)
{
mUndoStack.beginMacro("Select");
mUndoStack.push(new CClearSelectionCommand(this, mSelection));
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection));
mUndoStack.endMacro();
if (mSelection.empty())
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection));
else if ((mSelection.size() == 1) && (mSelection.front() == pNode))
return;
else
{
mUndoStack.beginMacro("Select");
mUndoStack.push(new CClearSelectionCommand(this, mSelection));
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection));
mUndoStack.endMacro();
}
}
}
void INodeEditor::SelectAll(FNodeFlags NodeFlags)
{
if (!mSelectionLocked)
mUndoStack.push(new CSelectAllCommand(this, mSelection, &mScene, NodeFlags));
}
void INodeEditor::InvertSelection(FNodeFlags NodeFlags)
{
if (!mSelectionLocked)
mUndoStack.push(new CInvertSelectionCommand(this, mSelection, &mScene, NodeFlags));
}
void INodeEditor::SetSelectionLocked(bool Locked)
{
mSelectionLocked = Locked;
}
// ************ PUBLIC SLOTS ************
void INodeEditor::OnGizmoMoved()
{

View File

@ -25,6 +25,7 @@ protected:
CScene mScene;
QList<CSceneNode*> mSelection;
CAABox mSelectionBounds;
bool mSelectionLocked;
// Gizmo
CGizmo mGizmo;
@ -56,6 +57,9 @@ public:
void DeselectNode(CSceneNode *pNode);
void ClearSelection();
void ClearAndSelectNode(CSceneNode *pNode);
void SelectAll(FNodeFlags NodeFlags);
void InvertSelection(FNodeFlags NodeFlags);
void SetSelectionLocked(bool Locked);
signals:
void SelectionModified();

View File

@ -0,0 +1,56 @@
#include "CInvertSelectionCommand.h"
#include <Core/Scene/CSceneIterator.h>
CInvertSelectionCommand::CInvertSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags)
: QUndoCommand("Invert Selection")
, mpEditor(pEditor)
, mOldSelection(rSelection)
, mpSelection(&rSelection)
{
CSceneIterator it(pScene, NodeFlags);
while (!it.DoneIterating())
{
if (!it->IsSelected())
mNewSelection.append(*it);
++it;
}
}
CInvertSelectionCommand::~CInvertSelectionCommand()
{
}
void CInvertSelectionCommand::undo()
{
*mpSelection = mOldSelection;
// Deselect all nodes in new selection
foreach (CSceneNode *pNode, mNewSelection)
pNode->SetSelected(false);
// Select all nodes in the old selection
foreach (CSceneNode *pNode, mOldSelection)
pNode->SetSelected(true);
// Update editor
mpEditor->RecalculateSelectionBounds();
mpEditor->SelectionModified();
}
void CInvertSelectionCommand::redo()
{
*mpSelection = mNewSelection;
// Deselect all nodes in the old selection
foreach (CSceneNode *pNode, mOldSelection)
pNode->SetSelected(false);
// Select all nodes in the new selection
foreach (CSceneNode *pNode, mNewSelection)
pNode->SetSelected(true);
// Update editor
mpEditor->RecalculateSelectionBounds();
mpEditor->SelectionModified();
}

View File

@ -0,0 +1,23 @@
#ifndef CINVERTSELECTIONCOMMAND_H
#define CINVERTSELECTIONCOMMAND_H
#include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h>
#include <QUndoCommand>
class CInvertSelectionCommand : public QUndoCommand
{
INodeEditor *mpEditor;
QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mNewSelection;
QList<CSceneNode*> *mpSelection;
public:
CInvertSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags);
~CInvertSelectionCommand();
void undo();
void redo();
};
#endif // CINVERTSELECTIONCOMMAND_H

View File

@ -0,0 +1,56 @@
#include "CSelectAllCommand.h"
#include <Core/Scene/CSceneIterator.h>
CSelectAllCommand::CSelectAllCommand(INodeEditor *pEditor, QList<CSceneNode *> &rSelection, CScene *pScene, FNodeFlags NodeFlags)
: QUndoCommand("Select All")
, mpEditor(pEditor)
, mOldSelection(rSelection)
, mpSelection(&rSelection)
{
CSceneIterator it(pScene, NodeFlags);
while (!it.DoneIterating())
{
mNewSelection.append(*it);
++it;
}
}
CSelectAllCommand::~CSelectAllCommand()
{
}
void CSelectAllCommand::undo()
{
*mpSelection = mOldSelection;
// Deselect all nodes in new selection
foreach (CSceneNode *pNode, mNewSelection)
pNode->SetSelected(false);
// Select all nodes in the old selection
foreach (CSceneNode *pNode, mOldSelection)
pNode->SetSelected(true);
// Update editor
mpEditor->RecalculateSelectionBounds();
mpEditor->SelectionModified();
}
void CSelectAllCommand::redo()
{
*mpSelection = mNewSelection;
// Deselect all nodes in the old selection
foreach (CSceneNode *pNode, mOldSelection)
pNode->SetSelected(false);
// Select all nodes in the new selection
foreach (CSceneNode *pNode, mNewSelection)
pNode->SetSelected(true);
// Update editor
mpEditor->RecalculateSelectionBounds();
mpEditor->SelectionModified();
}

View File

@ -0,0 +1,23 @@
#ifndef CSELECTALLCOMMAND_H
#define CSELECTALLCOMMAND_H
#include "Editor/INodeEditor.h"
#include <Core/Scene/CSceneNode.h>
#include <QUndoCommand>
class CSelectAllCommand : public QUndoCommand
{
INodeEditor *mpEditor;
QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mNewSelection;
QList<CSceneNode*> *mpSelection;
public:
CSelectAllCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags);
~CSelectAllCommand();
void undo();
void redo();
};
#endif // CSELECTALLCOMMAND_H

View File

@ -7,6 +7,8 @@
#include "CSelectNodeCommand.h"
#include "CDeselectNodeCommand.h"
#include "CClearSelectionCommand.h"
#include "CSelectAllCommand.h"
#include "CInvertSelectionCommand.h"
#include "EUndoCommand.h"
#endif // UNDOCOMMANDS

View File

@ -11,6 +11,7 @@
#include "Editor/UICommon.h"
#include <Core/Render/CDrawUtil.h>
#include <Core/Scene/CSceneIterator.h>
#include <Core/Log.h>
#include <iostream>
@ -57,7 +58,8 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
mpTransformCombo->setMinimumWidth(75);
ui->MainToolBar->addActions(mGizmoActions);
ui->MainToolBar->addWidget(mpTransformCombo);
ui->menuEdit->addActions(mUndoActions);
ui->menuEdit->insertActions(ui->ActionSelectAll, mUndoActions);
ui->menuEdit->insertSeparator(ui->ActionSelectAll);
// Initialize offscreen actions
addAction(ui->ActionIncrementGizmo);
@ -471,3 +473,17 @@ void CWorldEditor::on_ActionGameMode_triggered()
{
ui->MainViewport->SetGameMode(ui->ActionGameMode->isChecked());
}
void CWorldEditor::on_ActionSelectAll_triggered()
{
FNodeFlags NodeFlags = CScene::NodeFlagsForShowFlags(ui->MainViewport->ShowFlags());
NodeFlags &= ~(eStaticNode | eCollisionNode);
SelectAll(NodeFlags);
}
void CWorldEditor::on_ActionInvertSelection_triggered()
{
FNodeFlags NodeFlags = CScene::NodeFlagsForShowFlags(ui->MainViewport->ShowFlags());
NodeFlags &= ~(eStaticNode | eCollisionNode);
InvertSelection(NodeFlags);
}

View File

@ -77,6 +77,8 @@ private slots:
void on_ActionDecrementGizmo_triggered();
void on_ActionDrawObjectCollision_triggered();
void on_ActionGameMode_triggered();
void on_ActionSelectAll_triggered();
void on_ActionInvertSelection_triggered();
};
#endif // CWORLDEDITOR_H

View File

@ -245,6 +245,8 @@
<property name="title">
<string>Edit</string>
</property>
<addaction name="ActionSelectAll"/>
<addaction name="ActionInvertSelection"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
@ -740,6 +742,22 @@
<string>G</string>
</property>
</action>
<action name="ActionSelectAll">
<property name="text">
<string>Select All</string>
</property>
<property name="shortcut">
<string>Ctrl+A</string>
</property>
</action>
<action name="ActionInvertSelection">
<property name="text">
<string>Invert Selection</string>
</property>
<property name="shortcut">
<string>Ctrl+I</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -2,9 +2,6 @@
#include "CDarkStyle.h"
#include <Core/Resource/Factory/CTemplateLoader.h>
// temp
#include <Core/Resource/Cooker/CTemplateWriter.h>
#include <QApplication>
#include <QStyleFactory>