Added support for editing and resaving EGMC files + improved its preview rendering

This commit is contained in:
parax0 2016-01-16 01:13:27 -07:00
parent c0b74c9883
commit 5c3a37ca4a
20 changed files with 605 additions and 134 deletions

View File

@ -184,7 +184,8 @@ HEADERS += \
Scene/CSceneIterator.h \ Scene/CSceneIterator.h \
Resource/CResourceInfo.h \ Resource/CResourceInfo.h \
Resource/CPoiToWorld.h \ Resource/CPoiToWorld.h \
Resource/Factory/CPoiToWorldLoader.h Resource/Factory/CPoiToWorldLoader.h \
Resource/Cooker/CPoiToWorldCooker.h
# Source Files # Source Files
SOURCES += \ SOURCES += \
@ -272,4 +273,5 @@ SOURCES += \
Scene/CScene.cpp \ Scene/CScene.cpp \
Scene/CSceneIterator.cpp \ Scene/CSceneIterator.cpp \
Resource/CPoiToWorld.cpp \ Resource/CPoiToWorld.cpp \
Resource/Factory/CPoiToWorldLoader.cpp Resource/Factory/CPoiToWorldLoader.cpp \
Resource/Cooker/CPoiToWorldCooker.cpp

View File

@ -6,22 +6,74 @@ CPoiToWorld::CPoiToWorld()
CPoiToWorld::~CPoiToWorld() CPoiToWorld::~CPoiToWorld()
{ {
for (auto it = mMaps.begin(); it != mMaps.end(); it++)
delete *it;
} }
void CPoiToWorld::LinksForMeshID(std::list<u32>& rOutInstanceIDs, u32 MeshID) void CPoiToWorld::AddPoi(u32 PoiID)
{ {
for (u32 iLink = 0; iLink < mMeshLinks.size(); iLink++) // Check if this POI already exists
auto it = mPoiLookupMap.find(PoiID);
if (it == mPoiLookupMap.end())
{ {
if (mMeshLinks[iLink].MeshID == MeshID) SPoiMap *pMap = new SPoiMap();
rOutInstanceIDs.push_back(mMeshLinks[iLink].PoiInstanceID); pMap->PoiID = PoiID;
mMaps.push_back(pMap);
mPoiLookupMap[PoiID] = pMap;
} }
} }
void CPoiToWorld::LinksForInstanceID(std::list<u32>& rOutMeshIDs, u32 InstanceID) void CPoiToWorld::AddPoiMeshMap(u32 PoiID, u32 ModelID)
{ {
for (u32 iLink = 0; iLink < mMeshLinks.size(); iLink++) // Make sure the POI exists; the add function won't do anything if it does
AddPoi(PoiID);
SPoiMap *pMap = mPoiLookupMap[PoiID];
// Check whether this model ID is already mapped to this POI
for (auto it = pMap->ModelIDs.begin(); it != pMap->ModelIDs.end(); it++)
{ {
if (mMeshLinks[iLink].PoiInstanceID == InstanceID) if (*it == ModelID)
rOutMeshIDs.push_back(mMeshLinks[iLink].MeshID); return;
}
// We didn't return, so this is a new mapping
pMap->ModelIDs.push_back(ModelID);
}
void CPoiToWorld::RemovePoi(u32 PoiID)
{
for (auto it = mMaps.begin(); it != mMaps.end(); it++)
{
if ((*it)->PoiID == PoiID)
{
mMaps.erase(it);
mPoiLookupMap.erase(PoiID);
return;
}
}
}
void CPoiToWorld::RemovePoiMeshMap(u32 PoiID, u32 ModelID)
{
auto MapIt = mPoiLookupMap.find(PoiID);
if (MapIt != mPoiLookupMap.end())
{
SPoiMap *pMap = MapIt->second;
for (auto ListIt = pMap->ModelIDs.begin(); ListIt != pMap->ModelIDs.end(); ListIt++)
{
if (*ListIt == ModelID)
{
pMap->ModelIDs.erase(ListIt);
if (pMap->ModelIDs.empty())
RemovePoi(PoiID);
break;
}
}
} }
} }

View File

@ -3,37 +3,41 @@
#include "CResource.h" #include "CResource.h"
#include <list> #include <list>
#include <map>
#include <vector>
class CPoiToWorld : public CResource class CPoiToWorld : public CResource
{ {
DECLARE_RESOURCE_TYPE(ePoiToWorld) DECLARE_RESOURCE_TYPE(ePoiToWorld)
friend class CPoiToWorldLoader;
public: public:
struct SPoiMeshLink struct SPoiMap
{ {
u32 MeshID; u32 PoiID;
u32 PoiInstanceID; std::list<u32> ModelIDs;
}; };
private: private:
std::vector<SPoiMeshLink> mMeshLinks; std::vector<SPoiMap*> mMaps;
std::map<u32,SPoiMap*> mPoiLookupMap;
public: public:
CPoiToWorld(); CPoiToWorld();
~CPoiToWorld(); ~CPoiToWorld();
void LinksForMeshID(std::list<u32>& rOutInstanceIDs, u32 MeshID); void AddPoi(u32 PoiID);
void LinksForInstanceID(std::list<u32>& rOutMeshIDs, u32 InstanceID); void AddPoiMeshMap(u32 PoiID, u32 ModelID);
void RemovePoi(u32 PoiID);
void RemovePoiMeshMap(u32 PoiID, u32 ModelID);
inline u32 NumMeshLinks() inline u32 NumMappedPOIs() const
{ {
return mMeshLinks.size(); return mMaps.size();
} }
inline const SPoiMeshLink& MeshLinkByIndex(u32 Index) inline const SPoiMap* MapByIndex(u32 Index) const
{ {
return mMeshLinks[Index]; return mMaps[Index];
} }
}; };

View File

@ -0,0 +1,36 @@
#include "CPoiToWorldCooker.h"
void CPoiToWorldCooker::WriteEGMC(CPoiToWorld *pPoiToWorld, IOutputStream& rOut)
{
// Create mappings list
struct SPoiMapping
{
u32 MeshID;
u32 PoiID;
};
std::vector<SPoiMapping> Mappings;
for (u32 iPoi = 0; iPoi < pPoiToWorld->NumMappedPOIs(); iPoi++)
{
const CPoiToWorld::SPoiMap *kpMap = pPoiToWorld->MapByIndex(iPoi);
for (auto it = kpMap->ModelIDs.begin(); it != kpMap->ModelIDs.end(); it++)
{
SPoiMapping Mapping;
Mapping.MeshID = *it;
Mapping.PoiID = kpMap->PoiID;
Mappings.push_back(Mapping);
}
}
// Write EGMC
rOut.WriteLong(Mappings.size());
for (u32 iMap = 0; iMap < Mappings.size(); iMap++)
{
rOut.WriteLong(Mappings[iMap].MeshID);
rOut.WriteLong(Mappings[iMap].PoiID);
}
rOut.WriteToBoundary(32, -1);
}

View File

@ -0,0 +1,14 @@
#ifndef CPOITOWORLDCOOKER_H
#define CPOITOWORLDCOOKER_H
#include "Core/Resource/CPoiToWorld.h"
#include <FileIO/FileIO.h>
class CPoiToWorldCooker
{
CPoiToWorldCooker() {}
public:
static void WriteEGMC(CPoiToWorld *pPoiToWorld, IOutputStream& rOut);
};
#endif // CPOITOWORLDCOOKER_H

View File

@ -239,12 +239,7 @@ void CModelLoader::LoadSurfaceHeaderPrime(IInputStream& Model, SSurface *pSurf)
pSurf->ReflectionDirection = CVector3f(Model); pSurf->ReflectionDirection = CVector3f(Model);
if (mVersion >= eEchoesDemo) if (mVersion >= eEchoesDemo)
{ Model.Seek(0x4, SEEK_CUR); // Skipping unknown values
Model.Seek(0x2, SEEK_CUR); // Skipping unknown value
pSurf->MeshID = Model.ReadShort();
}
else
pSurf->MeshID = -1;
bool HasAABox = (ExtraSize >= 0x18); // MREAs have a set of bounding box coordinates here. bool HasAABox = (ExtraSize >= 0x18); // MREAs have a set of bounding box coordinates here.

View File

@ -3,14 +3,13 @@
CPoiToWorld* CPoiToWorldLoader::LoadEGMC(IInputStream& rEGMC) CPoiToWorld* CPoiToWorldLoader::LoadEGMC(IInputStream& rEGMC)
{ {
CPoiToWorld *pOut = new CPoiToWorld(); CPoiToWorld *pOut = new CPoiToWorld();
u32 NumLinks = rEGMC.ReadLong(); u32 NumMappings = rEGMC.ReadLong();
for (u32 iLink = 0; iLink < NumLinks; iLink++) for (u32 iMap = 0; iMap < NumMappings; iMap++)
{ {
CPoiToWorld::SPoiMeshLink Link; u32 MeshID = rEGMC.ReadLong();
Link.MeshID = rEGMC.ReadLong(); u32 InstanceID = rEGMC.ReadLong();
Link.PoiInstanceID = rEGMC.ReadLong(); pOut->AddPoiMeshMap(InstanceID, MeshID);
pOut->mMeshLinks.push_back(Link);
} }
return pOut; return pOut;

View File

@ -10,6 +10,7 @@ CModelNode::CModelNode(CScene *pScene, CSceneNode *pParent, CModel *pModel) : CS
mScale = CVector3f(1.f); mScale = CVector3f(1.f);
mLightingEnabled = true; mLightingEnabled = true;
mForceAlphaOn = false; mForceAlphaOn = false;
mEnableScanOverlay = false;
mTintColor = CColor::skWhite; mTintColor = CColor::skWhite;
} }
@ -59,6 +60,18 @@ void CModelNode::Draw(FRenderOptions Options, int ComponentIndex, const SViewInf
mpModel->Draw(Options, mActiveMatSet); mpModel->Draw(Options, mActiveMatSet);
else else
mpModel->DrawSurface(Options, ComponentIndex, mActiveMatSet); mpModel->DrawSurface(Options, ComponentIndex, mActiveMatSet);
if (mEnableScanOverlay)
{
CDrawUtil::UseColorShader(mScanOverlayColor);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ZERO);
Options |= eNoMaterialSetup;
if (ComponentIndex < 0)
mpModel->Draw(Options, 0);
else
mpModel->DrawSurface(Options, ComponentIndex, mActiveMatSet);
}
} }
void CModelNode::DrawSelection() void CModelNode::DrawSelection()

View File

@ -11,6 +11,8 @@ class CModelNode : public CSceneNode
bool mLightingEnabled; bool mLightingEnabled;
bool mForceAlphaOn; bool mForceAlphaOn;
CColor mTintColor; CColor mTintColor;
bool mEnableScanOverlay;
CColor mScanOverlayColor;
public: public:
explicit CModelNode(CScene *pScene, CSceneNode *pParent = 0, CModel *pModel = 0); explicit CModelNode(CScene *pScene, CSceneNode *pParent = 0, CModel *pModel = 0);
@ -31,6 +33,8 @@ public:
inline void ForceAlphaEnabled(bool Enable) { mForceAlphaOn = Enable; } inline void ForceAlphaEnabled(bool Enable) { mForceAlphaOn = Enable; }
inline void SetTintColor(const CColor& rkTintColor) { mTintColor = rkTintColor; } inline void SetTintColor(const CColor& rkTintColor) { mTintColor = rkTintColor; }
inline void ClearTintColor() { mTintColor = CColor::skWhite; } inline void ClearTintColor() { mTintColor = CColor::skWhite; }
inline void SetScanOverlayEnabled(bool Enable) { mEnableScanOverlay = Enable; }
inline void SetScanOverlayColor(const CColor& rkColor) { mScanOverlayColor = rkColor; }
inline CModel* Model() const { return mpModel; } inline CModel* Model() const { return mpModel; }
inline u32 MatSet() const { return mActiveMatSet; } inline u32 MatSet() const { return mActiveMatSet; }
inline bool IsDynamicLightingEnabled() const { return mLightingEnabled; } inline bool IsDynamicLightingEnabled() const { return mLightingEnabled; }

View File

@ -346,41 +346,7 @@ void CSceneViewport::OnMouseRelease(QMouseEvent *pEvent)
// Object selection/deselection // Object selection/deselection
else else
{ emit ViewportClick(mpHoverNode, pEvent);
bool validNode = (mpHoverNode && (mpHoverNode->NodeType() != eStaticNode) && (mpHoverNode->NodeType() != eModelNode));
bool altPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0);
bool ctrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0);
// Alt: Deselect
if (altPressed)
{
if (!validNode)
return;
mpEditor->DeselectNode(mpHoverNode);
}
// Ctrl: Add to selection
else if (ctrlPressed)
{
if (validNode)
mpEditor->SelectNode(mpHoverNode);
}
// Neither: clear selection + select
else
{
if (!mGizmoHovering)
{
if (validNode)
mpEditor->ClearAndSelectNode(mpHoverNode);
else
mpEditor->ClearSelection();
}
}
mpEditor->UpdateSelectionUI();
}
} }
} }

View File

@ -56,6 +56,7 @@ protected:
void CreateContextMenu(); void CreateContextMenu();
signals: signals:
void ViewportClick(CSceneNode *pNode, QMouseEvent *pEvent);
void GizmoMoved(); void GizmoMoved();
void CameraOrbit(); void CameraOrbit();

View File

@ -1,8 +1,11 @@
#include "INodeEditor.h" #include "INodeEditor.h"
#include "Editor/Undo/UndoCommands.h" #include "Editor/Undo/UndoCommands.h"
#include <QMouseEvent>
INodeEditor::INodeEditor(QWidget *pParent) INodeEditor::INodeEditor(QWidget *pParent)
: QMainWindow(pParent) : QMainWindow(pParent)
, mPickMode(false)
, mSelectionNodeFlags(eAllNodeTypes)
, mSelectionLocked(false) , mSelectionLocked(false)
, mShowGizmo(false) , mShowGizmo(false)
, mGizmoHovering(false) , mGizmoHovering(false)
@ -206,6 +209,28 @@ const QList<CSceneNode*>& INodeEditor::GetSelection() const
return mSelection; return mSelection;
} }
void INodeEditor::EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, QCursor Cursor /*= Qt::CrossCursor*/)
{
// If we're already in pick mode, exit first so the previous caller has a chance to disconnect
if (mPickMode)
ExitPickMode();
mPickMode = true;
mAllowedPickNodes = AllowedNodes;
mExitOnInvalidPick = ExitOnInvalidPick;
mEmitOnInvalidPick = EmitOnInvalidPick;
emit PickModeEntered(Cursor);
}
void INodeEditor::ExitPickMode()
{
if (mPickMode)
{
mPickMode = false;
emit PickModeExited();
}
}
// ************ PUBLIC SLOTS ************ // ************ PUBLIC SLOTS ************
void INodeEditor::OnGizmoMoved() void INodeEditor::OnGizmoMoved()
{ {
@ -237,6 +262,60 @@ void INodeEditor::OnGizmoMoved()
UpdateGizmoUI(); UpdateGizmoUI();
} }
// ************ PROTECTED SLOTS ************
void INodeEditor::OnViewportClick(CSceneNode *pHoverNode, QMouseEvent *pEvent)
{
// Not in pick mode: process node selection/deselection
if (!mPickMode)
{
bool ValidNode = (pHoverNode && (pHoverNode->NodeType() & mSelectionNodeFlags));
bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0);
bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0);
// Alt: Deselect
if (AltPressed)
{
if (!ValidNode)
return;
DeselectNode(pHoverNode);
}
// Ctrl: Add to selection
else if (CtrlPressed)
{
if (ValidNode)
SelectNode(pHoverNode);
}
// Neither: clear selection + select
else
{
if (!mGizmoHovering)
{
if (ValidNode)
ClearAndSelectNode(pHoverNode);
else
ClearSelection();
}
}
UpdateSelectionUI();
}
// In pick mode: process node pick
else
{
bool ValidNode = (pHoverNode && (pHoverNode->NodeType() & mAllowedPickNodes));
if (ValidNode || mEmitOnInvalidPick)
emit PickModeClick(pHoverNode, pEvent);
if (!ValidNode && mExitOnInvalidPick)
ExitPickMode();
}
}
// ************ PRIVATE ************ // ************ PRIVATE ************
void INodeEditor::UpdateTransformActionsEnabled() void INodeEditor::UpdateTransformActionsEnabled()
{ {

View File

@ -24,6 +24,7 @@ protected:
// Node management // Node management
CScene mScene; CScene mScene;
QList<CSceneNode*> mSelection; QList<CSceneNode*> mSelection;
FNodeFlags mSelectionNodeFlags;
CAABox mSelectionBounds; CAABox mSelectionBounds;
bool mSelectionLocked; bool mSelectionLocked;
@ -40,6 +41,12 @@ protected:
QList<QAction*> mGizmoActions; QList<QAction*> mGizmoActions;
QComboBox *mpTransformCombo; QComboBox *mpTransformCombo;
// Pick mode
bool mPickMode;
bool mExitOnInvalidPick;
bool mEmitOnInvalidPick;
FNodeFlags mAllowedPickNodes;
public: public:
explicit INodeEditor(QWidget *pParent = 0); explicit INodeEditor(QWidget *pParent = 0);
virtual ~INodeEditor(); virtual ~INodeEditor();
@ -63,10 +70,17 @@ public:
bool HasSelection() const; bool HasSelection() const;
const QList<CSceneNode*>& GetSelection() const; const QList<CSceneNode*>& GetSelection() const;
void EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, QCursor Cursor = Qt::CrossCursor);
void ExitPickMode();
signals: signals:
void SelectionModified(); void SelectionModified();
void SelectionTransformed(); void SelectionTransformed();
void PickModeEntered(QCursor Cursor);
void PickModeExited();
void PickModeClick(CSceneNode *pNode, QMouseEvent *pEvent);
public slots: public slots:
void OnGizmoMoved(); void OnGizmoMoved();
virtual void UpdateGizmoUI() = 0; virtual void UpdateGizmoUI() = 0;
@ -75,6 +89,9 @@ public slots:
protected: protected:
virtual void GizmoModeChanged(CGizmo::EGizmoMode /*mode*/) {} virtual void GizmoModeChanged(CGizmo::EGizmoMode /*mode*/) {}
protected slots:
void OnViewportClick(CSceneNode *pHoverNode, QMouseEvent *pEvent);
private: private:
void UpdateTransformActionsEnabled(); void UpdateTransformActionsEnabled();

View File

@ -1,15 +1,25 @@
#include "CPoiMapEditDialog.h" #include "CPoiMapEditDialog.h"
#include "ui_CPoiMapEditDialog.h" #include "ui_CPoiMapEditDialog.h"
#include "CWorldEditor.h" #include "CWorldEditor.h"
#include "Editor/UICommon.h"
#include <Core/Resource/CScan.h> #include <Core/Resource/CScan.h>
#include <Core/Resource/Cooker/CPoiToWorldCooker.h>
#include <Core/ScriptExtra/CPointOfInterestExtra.h> #include <Core/ScriptExtra/CPointOfInterestExtra.h>
#include <QMouseEvent>
#include <QMessageBox>
const CColor CPoiMapEditDialog::skNormalColor(0.137255f, 0.184314f, 0.776471f, 0.5f);
const CColor CPoiMapEditDialog::skImportantColor(0.721569f, 0.066667f, 0.066667f, 0.5f);
CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent) CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::CPoiMapEditDialog) , ui(new Ui::CPoiMapEditDialog)
, mpEditor(pEditor) , mpEditor(pEditor)
, mSourceModel(pEditor, this) , mSourceModel(pEditor, this)
, mHighlightMode(eHighlightSelected) , mHighlightMode(eHighlightSelected)
, mPickType(eNotPicking)
{ {
mModel.setSourceModel(&mSourceModel); mModel.setSourceModel(&mSourceModel);
mModel.sort(0); mModel.sort(0);
@ -29,8 +39,11 @@ CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent)
connect(ui->ListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), connect(ui->ListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(OnSelectionChanged(QItemSelection,QItemSelection))); this, SLOT(OnSelectionChanged(QItemSelection,QItemSelection)));
connect(ui->ListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnItemDoubleClick(QModelIndex))); connect(ui->ListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnItemDoubleClick(QModelIndex)));
connect(ui->ButtonBox, SIGNAL(accepted()), this, SLOT(close())); connect(ui->AddMeshButton, SIGNAL(clicked()), this, SLOT(PickButtonClicked()));
connect(ui->ButtonBox, SIGNAL(rejected()), this, SLOT(close())); connect(ui->RemoveMeshButton, SIGNAL(clicked()), this, SLOT(PickButtonClicked()));
connect(ui->ButtonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(close()));
connect(ui->ButtonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(close()));
connect(ui->ButtonBox->button(QDialogButtonBox::Save), SIGNAL(clicked()), this, SLOT(Save()));
} }
CPoiMapEditDialog::~CPoiMapEditDialog() CPoiMapEditDialog::~CPoiMapEditDialog()
@ -40,25 +53,33 @@ CPoiMapEditDialog::~CPoiMapEditDialog()
// Clear model tints // Clear model tints
if (mHighlightMode != eHighlightNone) if (mHighlightMode != eHighlightNone)
SetHighlightNone(); SetHighlightNone();
// Stop picking
if (mPickType != eNotPicking)
StopPicking();
}
void CPoiMapEditDialog::closeEvent(QCloseEvent* /*pEvent*/)
{
if (mPickType != eNotPicking)
mpEditor->ExitPickMode();
emit Closed();
} }
void CPoiMapEditDialog::HighlightPoiModels(const QModelIndex& rkIndex) void CPoiMapEditDialog::HighlightPoiModels(const QModelIndex& rkIndex)
{ {
// Get POI and models // Get POI and models
QModelIndex SourceIndex = mModel.mapToSource(rkIndex); QModelIndex SourceIndex = mModel.mapToSource(rkIndex);
CScriptNode *pPOI = mSourceModel.PoiNodePointer(SourceIndex); const QList<CModelNode*>& rkModels = mSourceModel.GetPoiMeshList(SourceIndex);
const QList<CModelNode*>& rkModels = mSourceModel.GetPoiMeshList(pPOI); bool Important = IsImportant(SourceIndex);
// Check whether this is an important scan
bool IsImportant = false;
TResPtr<CScan> pScan = static_cast<CPointOfInterestExtra*>(pPOI->Extra())->GetScan();
if (pScan)
IsImportant = pScan->IsImportant();
// Highlight the meshes // Highlight the meshes
for (int iMdl = 0; iMdl < rkModels.size(); iMdl++) for (int iMdl = 0; iMdl < rkModels.size(); iMdl++)
rkModels[iMdl]->SetTintColor(IsImportant ? CColor::skRed : CColor::skBlue); {
rkModels[iMdl]->SetScanOverlayEnabled(true);
rkModels[iMdl]->SetScanOverlayColor(Important ? skImportantColor : skNormalColor);
}
} }
void CPoiMapEditDialog::UnhighlightPoiModels(const QModelIndex& rkIndex) void CPoiMapEditDialog::UnhighlightPoiModels(const QModelIndex& rkIndex)
@ -67,7 +88,48 @@ void CPoiMapEditDialog::UnhighlightPoiModels(const QModelIndex& rkIndex)
const QList<CModelNode*>& rkModels = mSourceModel.GetPoiMeshList(SourceIndex); const QList<CModelNode*>& rkModels = mSourceModel.GetPoiMeshList(SourceIndex);
for (int iMdl = 0; iMdl < rkModels.size(); iMdl++) for (int iMdl = 0; iMdl < rkModels.size(); iMdl++)
rkModels[iMdl]->ClearTintColor(); rkModels[iMdl]->SetScanOverlayEnabled(false);
}
void CPoiMapEditDialog::HighlightModel(const QModelIndex& rkIndex, CModelNode *pNode)
{
bool Important = IsImportant(rkIndex);
pNode->SetScanOverlayEnabled(true);
pNode->SetScanOverlayColor(Important ? skImportantColor : skNormalColor);
}
void CPoiMapEditDialog::UnhighlightModel(CModelNode *pNode)
{
pNode->SetScanOverlayEnabled(false);
}
bool CPoiMapEditDialog::IsImportant(const QModelIndex& rkIndex)
{
CScriptNode *pPOI = mSourceModel.PoiNodePointer(rkIndex);
bool Important = false;
TResPtr<CScan> pScan = static_cast<CPointOfInterestExtra*>(pPOI->Extra())->GetScan();
if (pScan)
Important = pScan->IsImportant();
return Important;
}
void CPoiMapEditDialog::Save()
{
CPoiToWorld *pPoiToWorld = mpEditor->ActiveArea()->GetPoiToWorldMap();
TString FileName = pPoiToWorld->FullSource();
CFileOutStream Out(FileName.ToStdString(), IOUtil::eBigEndian);
if (Out.IsValid())
{
CPoiToWorldCooker::WriteEGMC(pPoiToWorld, Out);
QMessageBox::information(this, "Saved", QString("Saved to %1!").arg(TO_QSTRING(pPoiToWorld->Source())));
}
else
QMessageBox::warning(this, "Error", "Couldn't save EGMC; unable to open output file");
} }
void CPoiMapEditDialog::SetHighlightSelected() void CPoiMapEditDialog::SetHighlightSelected()
@ -127,3 +189,81 @@ void CPoiMapEditDialog::OnItemDoubleClick(QModelIndex Index)
CScriptNode *pPOI = mSourceModel.PoiNodePointer(SourceIndex); CScriptNode *pPOI = mSourceModel.PoiNodePointer(SourceIndex);
mpEditor->ClearAndSelectNode(pPOI); mpEditor->ClearAndSelectNode(pPOI);
} }
void CPoiMapEditDialog::PickButtonClicked()
{
QPushButton *pButton = qobject_cast<QPushButton*>(sender());
if (!pButton->isChecked())
mpEditor->ExitPickMode();
else
{
mpEditor->EnterPickMode(eModelNode, false, false);
connect(mpEditor, SIGNAL(PickModeExited()), this, SLOT(StopPicking()));
connect(mpEditor, SIGNAL(PickModeClick(CSceneNode*,QMouseEvent*)), this, SLOT(OnNodePicked(CSceneNode*,QMouseEvent*)));
pButton->setChecked(true);
if (pButton == ui->AddMeshButton)
{
mPickType = eAddMeshes;
ui->RemoveMeshButton->setChecked(false);
}
else if (pButton == ui->RemoveMeshButton)
{
mPickType = eRemoveMeshes;
ui->AddMeshButton->setChecked(false);
}
}
}
void CPoiMapEditDialog::StopPicking()
{
ui->AddMeshButton->setChecked(false);
ui->RemoveMeshButton->setChecked(false);
mPickType = eNotPicking;
disconnect(mpEditor, 0, this, 0);
}
void CPoiMapEditDialog::OnNodePicked(CSceneNode *pNode, QMouseEvent* pEvent)
{
// Check for valid selection
QModelIndexList Indices = ui->ListView->selectionModel()->selectedRows();
if (Indices.isEmpty()) return;
// Map selection to source model
QModelIndexList SourceIndices;
for (auto it = Indices.begin(); it != Indices.end(); it++)
SourceIndices << mModel.mapToSource(*it);
// If alt is pressed, invert the pick mode
CModelNode *pModel = static_cast<CModelNode*>(pNode);
bool AltPressed = (pEvent->modifiers() & Qt::AltModifier) != 0;
EPickType PickType;
if (!AltPressed) PickType = mPickType;
else if (mPickType == eAddMeshes) PickType = eRemoveMeshes;
else PickType = eAddMeshes;
// Add meshes
if (PickType == eAddMeshes)
{
for (auto it = SourceIndices.begin(); it != SourceIndices.end(); it++)
mSourceModel.AddMapping(*it, pModel);
if (mHighlightMode != eHighlightNone)
HighlightModel(SourceIndices.front(), pModel);
}
// Remove meshes
else if (PickType == eRemoveMeshes)
{
for (auto it = SourceIndices.begin(); it != SourceIndices.end(); it++)
mSourceModel.RemoveMapping(*it, pModel);
if (mHighlightMode != eHighlightNone)
UnhighlightModel(pModel);
}
}

View File

@ -27,20 +27,40 @@ class CPoiMapEditDialog : public QMainWindow
QSortFilterProxyModel mModel; QSortFilterProxyModel mModel;
EHighlightMode mHighlightMode; EHighlightMode mHighlightMode;
enum EPickType
{
eNotPicking,
eAddMeshes,
eRemoveMeshes,
eAddPOIs
} mPickType;
static const CColor skNormalColor;
static const CColor skImportantColor;
public: public:
explicit CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent = 0); explicit CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent = 0);
~CPoiMapEditDialog(); ~CPoiMapEditDialog();
void closeEvent(QCloseEvent *) { emit Closed(); } void closeEvent(QCloseEvent *pEvent);
void HighlightPoiModels(const QModelIndex& rkIndex); void HighlightPoiModels(const QModelIndex& rkIndex);
void UnhighlightPoiModels(const QModelIndex& rkIndex); void UnhighlightPoiModels(const QModelIndex& rkIndex);
void HighlightModel(const QModelIndex& rkIndex, CModelNode *pNode);
void UnhighlightModel(CModelNode *pNode);
void RefreshHighlights();
bool IsImportant(const QModelIndex& rkIndex);
public slots: public slots:
void Save();
void SetHighlightSelected(); void SetHighlightSelected();
void SetHighlightAll(); void SetHighlightAll();
void SetHighlightNone(); void SetHighlightNone();
void OnSelectionChanged(const QItemSelection& rkSelected, const QItemSelection& rkDeselected); void OnSelectionChanged(const QItemSelection& rkSelected, const QItemSelection& rkDeselected);
void OnItemDoubleClick(QModelIndex Index); void OnItemDoubleClick(QModelIndex Index);
void PickButtonClicked();
void StopPicking();
void OnNodePicked(CSceneNode *pNode, QMouseEvent *pEvent);
signals: signals:
void Closed(); void Closed();
}; };

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>469</width> <width>274</width>
<height>327</height> <height>308</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -28,10 +28,55 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="AddMeshButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../Icons.qrc">
<normaloff>:/icons/Plus.png</normaloff>:/icons/Plus.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="RemoveMeshButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../Icons.qrc">
<normaloff>:/icons/Minus v2.png</normaloff>:/icons/Minus v2.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item> <item>
<widget class="QDialogButtonBox" name="ButtonBox"> <widget class="QDialogButtonBox" name="ButtonBox">
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Save</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -88,6 +133,8 @@
</property> </property>
</action> </action>
</widget> </widget>
<resources/> <resources>
<include location="../Icons.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -7,13 +7,12 @@
CPoiMapModel::CPoiMapModel(CWorldEditor *pEditor, QObject *pParent /*= 0*/) CPoiMapModel::CPoiMapModel(CWorldEditor *pEditor, QObject *pParent /*= 0*/)
: QAbstractListModel(pParent) : QAbstractListModel(pParent)
, mpEditor(pEditor) , mpEditor(pEditor)
, mpArea(pEditor->ActiveArea())
, mpPoiToWorld(mpArea->GetPoiToWorldMap())
{ {
mpEditor = pEditor;
mpPoiToWorld = mpEditor->ActiveArea()->GetPoiToWorldMap();
if (mpPoiToWorld) if (mpPoiToWorld)
{ {
// Create map of model nodes // Create an ID -> Model Node lookup map
QMap<u32,CModelNode*> NodeMap; QMap<u32,CModelNode*> NodeMap;
for (CSceneIterator It(mpEditor->Scene(), eModelNode, true); !It.DoneIterating(); ++It) for (CSceneIterator It(mpEditor->Scene(), eModelNode, true); !It.DoneIterating(); ++It)
@ -22,24 +21,34 @@ CPoiMapModel::CPoiMapModel(CWorldEditor *pEditor, QObject *pParent /*= 0*/)
NodeMap[pNode->FindMeshID()] = pNode; NodeMap[pNode->FindMeshID()] = pNode;
} }
// Create list of mappings // Create internal model map
for (u32 iMap = 0; iMap < mpPoiToWorld->NumMeshLinks(); iMap++) for (u32 iPoi = 0; iPoi < mpPoiToWorld->NumMappedPOIs(); iPoi++)
{ {
const CPoiToWorld::SPoiMeshLink& rkLink = mpPoiToWorld->MeshLinkByIndex(iMap); const CPoiToWorld::SPoiMap *pkMap = mpPoiToWorld->MapByIndex(iPoi);
CScriptNode *pPOI = mpEditor->Scene()->ScriptNodeByID(rkLink.PoiInstanceID); CScriptNode *pPoiNode = mpEditor->Scene()->ScriptNodeByID(pkMap->PoiID);
if (!mPoiLookupMap.contains(pPOI)) if (pPoiNode)
{ {
SEditorPoiMap Map; QList<CModelNode*> *pModelList = new QList<CModelNode*>;
Map.pPOI = pPOI;
mMaps << Map; for (auto it = pkMap->ModelIDs.begin(); it != pkMap->ModelIDs.end(); it++)
mPoiLookupMap[pPOI] = &mMaps.last(); {
if (NodeMap.contains(*it))
*pModelList << NodeMap[*it];
} }
if (NodeMap.contains(rkLink.MeshID)) mModelMap[pPoiNode] = pModelList;
mPoiLookupMap[pPOI]->Models << NodeMap[rkLink.MeshID];
} }
} }
}
}
CPoiMapModel::~CPoiMapModel()
{
QList<QList<CModelNode*>*> Lists = mModelMap.values();
for (auto it = Lists.begin(); it != Lists.end(); it++)
delete *it;
} }
QVariant CPoiMapModel::headerData(int Section, Qt::Orientation Orientation, int Role) const QVariant CPoiMapModel::headerData(int Section, Qt::Orientation Orientation, int Role) const
@ -52,31 +61,33 @@ QVariant CPoiMapModel::headerData(int Section, Qt::Orientation Orientation, int
int CPoiMapModel::rowCount(const QModelIndex& /*rkParent*/) const int CPoiMapModel::rowCount(const QModelIndex& /*rkParent*/) const
{ {
return mMaps.size(); return mpPoiToWorld->NumMappedPOIs();
} }
QVariant CPoiMapModel::data(const QModelIndex& rkIndex, int Role) const QVariant CPoiMapModel::data(const QModelIndex& rkIndex, int Role) const
{ {
if (rkIndex.row() < mMaps.size()) if (rkIndex.row() < rowCount(QModelIndex()))
{ {
const SEditorPoiMap& rkMap = mMaps[rkIndex.row()]; const CPoiToWorld::SPoiMap *pkMap = mpPoiToWorld->MapByIndex(rkIndex.row());
CScriptObject *pPOI = mpArea->GetInstanceByID(pkMap->PoiID);
if (Role == Qt::DisplayRole) if (Role == Qt::DisplayRole)
{ {
if (rkMap.pPOI) if (pPOI)
return TO_QSTRING(rkMap.pPOI->Object()->InstanceName()); return TO_QSTRING(pPOI->InstanceName());
else else
return "[INVALID POI]"; return "[INVALID POI]";
} }
else if (Role == Qt::DecorationRole) else if (Role == Qt::DecorationRole)
{ {
CScriptNode *pNode = mpEditor->Scene()->NodeForObject(pPOI);
bool IsImportant = false; bool IsImportant = false;
if (rkMap.pPOI) if (pNode)
{ {
// Get scan // Get scan
CScan *pScan = static_cast<CPointOfInterestExtra*>(rkMap.pPOI->Extra())->GetScan(); CScan *pScan = static_cast<CPointOfInterestExtra*>(pNode->Extra())->GetScan();
if (pScan) if (pScan)
IsImportant = pScan->IsImportant(); IsImportant = pScan->IsImportant();
@ -92,10 +103,66 @@ QVariant CPoiMapModel::data(const QModelIndex& rkIndex, int Role) const
return QVariant::Invalid; return QVariant::Invalid;
} }
void CPoiMapModel::AddPOI(CScriptNode *pPOI)
{
if (!mModelMap.contains(pPOI))
{
QList<CModelNode*> *pList = new QList<CModelNode*>;
mModelMap[pPOI] = pList;
mpPoiToWorld->AddPoi(pPOI->Object()->InstanceID());
}
}
void CPoiMapModel::AddMapping(const QModelIndex& rkIndex, CModelNode *pNode)
{
CScriptNode *pPOI = PoiNodePointer(rkIndex);
AddPOI(pPOI);
QList<CModelNode*> *pList = mModelMap[pPOI];
if (!pList->contains(pNode))
pList->append(pNode);
mpPoiToWorld->AddPoiMeshMap(pPOI->Object()->InstanceID(), pNode->FindMeshID());
}
void CPoiMapModel::RemovePOI(const QModelIndex& rkIndex)
{
CScriptNode *pPOI = PoiNodePointer(rkIndex);
if (mModelMap.contains(pPOI))
{
delete mModelMap[pPOI];
mModelMap.remove(pPOI);
}
mpPoiToWorld->RemovePoi(pPOI->Object()->InstanceID());
}
void CPoiMapModel::RemoveMapping(const QModelIndex& rkIndex, CModelNode *pNode)
{
CScriptNode *pPOI = PoiNodePointer(rkIndex);
if (mModelMap.contains(pPOI))
{
QList<CModelNode*> *pList = mModelMap[pPOI];
pList->removeOne(pNode);
if (pList->isEmpty())
RemovePOI(rkIndex);
else
mpPoiToWorld->RemovePoiMeshMap(pPOI->Object()->InstanceID(), pNode->FindMeshID());
}
else
mpPoiToWorld->RemovePoiMeshMap(pPOI->Object()->InstanceID(), pNode->FindMeshID());
}
CScriptNode* CPoiMapModel::PoiNodePointer(const QModelIndex& rkIndex) const CScriptNode* CPoiMapModel::PoiNodePointer(const QModelIndex& rkIndex) const
{ {
if (rkIndex.row() < mMaps.size()) if ((u32) rkIndex.row() < mpPoiToWorld->NumMappedPOIs())
return mMaps[rkIndex.row()].pPOI; {
const CPoiToWorld::SPoiMap *pkMap = mpPoiToWorld->MapByIndex(rkIndex.row());
return mpEditor->Scene()->ScriptNodeByID(pkMap->PoiID);
}
return nullptr; return nullptr;
} }
@ -108,5 +175,5 @@ const QList<CModelNode*>& CPoiMapModel::GetPoiMeshList(const QModelIndex& rkInde
const QList<CModelNode*>& CPoiMapModel::GetPoiMeshList(CScriptNode *pPOI) const const QList<CModelNode*>& CPoiMapModel::GetPoiMeshList(CScriptNode *pPOI) const
{ {
return mPoiLookupMap[pPOI]->Models; return *mModelMap[pPOI];
} }

View File

@ -14,26 +14,24 @@ class CPoiMapModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public:
private:
CWorldEditor *mpEditor; CWorldEditor *mpEditor;
CGameArea *mpArea;
TResPtr<CPoiToWorld> mpPoiToWorld; TResPtr<CPoiToWorld> mpPoiToWorld;
struct SEditorPoiMap QMap<CScriptNode*, QList<CModelNode*>*> mModelMap;
{
CScriptNode *pPOI;
QList<CModelNode*> Models;
};
QList<SEditorPoiMap> mMaps;
QMap<CScriptNode*,SEditorPoiMap*> mPoiLookupMap;
public: public:
explicit CPoiMapModel(CWorldEditor *pEditor, QObject *pParent = 0); explicit CPoiMapModel(CWorldEditor *pEditor, QObject *pParent = 0);
~CPoiMapModel();
QVariant headerData(int Section, Qt::Orientation Orientation, int Role) const; QVariant headerData(int Section, Qt::Orientation Orientation, int Role) const;
int rowCount(const QModelIndex& rkParent) const; int rowCount(const QModelIndex& rkParent) const;
QVariant data(const QModelIndex& rkIndex, int Role) const; QVariant data(const QModelIndex& rkIndex, int Role) const;
void AddPOI(CScriptNode *pPOI);
void AddMapping(const QModelIndex& rkIndex, CModelNode *pNode);
void RemovePOI(const QModelIndex& rkIndex);
void RemoveMapping(const QModelIndex& rkIndex, CModelNode *pNode);
CScriptNode* PoiNodePointer(const QModelIndex& rkIndex) const; CScriptNode* PoiNodePointer(const QModelIndex& rkIndex) const;
const QList<CModelNode*>& GetPoiMeshList(const QModelIndex& rkIndex) const; const QList<CModelNode*>& GetPoiMeshList(const QModelIndex& rkIndex) const;
const QList<CModelNode*>& GetPoiMeshList(CScriptNode *pPOI) const; const QList<CModelNode*>& GetPoiMeshList(CScriptNode *pPOI) const;

View File

@ -31,6 +31,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
mpPoiDialog = nullptr; mpPoiDialog = nullptr;
mGizmoHovering = false; mGizmoHovering = false;
mGizmoTransforming = false; mGizmoTransforming = false;
mSelectionNodeFlags = eScriptNode | eLightNode;
// Start refresh timer // Start refresh timer
connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport())); connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport()));
@ -67,10 +68,13 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
addAction(ui->ActionDecrementGizmo); addAction(ui->ActionDecrementGizmo);
// Connect signals and slots // Connect signals and slots
connect(ui->MainViewport, SIGNAL(ViewportClick(CSceneNode*,QMouseEvent*)), this, SLOT(OnViewportClick(CSceneNode*,QMouseEvent*)));
connect(ui->MainViewport, SIGNAL(GizmoMoved()), this, SLOT(OnGizmoMoved())); connect(ui->MainViewport, SIGNAL(GizmoMoved()), this, SLOT(OnGizmoMoved()));
connect(ui->MainViewport, SIGNAL(CameraOrbit()), this, SLOT(UpdateCameraOrbit())); connect(ui->MainViewport, SIGNAL(CameraOrbit()), this, SLOT(UpdateCameraOrbit()));
connect(this, SIGNAL(SelectionModified()), this, SLOT(UpdateCameraOrbit())); connect(this, SIGNAL(SelectionModified()), this, SLOT(UpdateCameraOrbit()));
connect(this, SIGNAL(SelectionTransformed()), this, SLOT(UpdateCameraOrbit())); connect(this, SIGNAL(SelectionTransformed()), this, SLOT(UpdateCameraOrbit()));
connect(this, SIGNAL(PickModeEntered(QCursor)), this, SLOT(OnPickModeEnter(QCursor)));
connect(this, SIGNAL(PickModeExited()), this, SLOT(OnPickModeExit()));
connect(ui->TransformSpinBox, SIGNAL(ValueChanged(CVector3f)), this, SLOT(OnTransformSpinBoxModified(CVector3f))); connect(ui->TransformSpinBox, SIGNAL(ValueChanged(CVector3f)), this, SLOT(OnTransformSpinBoxModified(CVector3f)));
connect(ui->TransformSpinBox, SIGNAL(EditingDone(CVector3f)), this, SLOT(OnTransformSpinBoxEdited(CVector3f))); connect(ui->TransformSpinBox, SIGNAL(EditingDone(CVector3f)), this, SLOT(OnTransformSpinBoxEdited(CVector3f)));
connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double))); connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double)));
@ -102,6 +106,7 @@ bool CWorldEditor::eventFilter(QObject *pObj, QEvent *pEvent)
void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea)
{ {
ExitPickMode();
ui->MainViewport->ResetHover(); ui->MainViewport->ResetHover();
ClearSelection(); ClearSelection();
ui->ModifyTabContents->ClearUI(); ui->ModifyTabContents->ClearUI();
@ -173,7 +178,7 @@ void CWorldEditor::UpdateStatusBar()
{ {
CSceneNode *pHoverNode = ui->MainViewport->HoverNode(); CSceneNode *pHoverNode = ui->MainViewport->HoverNode();
if (pHoverNode && (pHoverNode->NodeType() != eStaticNode) && (pHoverNode->NodeType() != eModelNode)) if (pHoverNode && (pHoverNode->NodeType() & mSelectionNodeFlags))
StatusText = TO_QSTRING(pHoverNode->Name()); StatusText = TO_QSTRING(pHoverNode->Name());
} }
} }
@ -266,13 +271,13 @@ void CWorldEditor::GizmoModeChanged(CGizmo::EGizmoMode mode)
void CWorldEditor::UpdateCursor() void CWorldEditor::UpdateCursor()
{ {
if (ui->MainViewport->IsCursorVisible()) if (ui->MainViewport->IsCursorVisible() && !mPickMode)
{ {
CSceneNode *pHoverNode = ui->MainViewport->HoverNode(); CSceneNode *pHoverNode = ui->MainViewport->HoverNode();
if (ui->MainViewport->IsHoveringGizmo()) if (ui->MainViewport->IsHoveringGizmo())
ui->MainViewport->SetCursorState(Qt::SizeAllCursor); ui->MainViewport->SetCursorState(Qt::SizeAllCursor);
else if ((pHoverNode) && (pHoverNode->NodeType() != eStaticNode) && (pHoverNode->NodeType() != eModelNode)) else if ((pHoverNode) && (pHoverNode->NodeType() & mSelectionNodeFlags))
ui->MainViewport->SetCursorState(Qt::PointingHandCursor); ui->MainViewport->SetCursorState(Qt::PointingHandCursor);
else else
ui->MainViewport->SetCursorState(Qt::ArrowCursor); ui->MainViewport->SetCursorState(Qt::ArrowCursor);
@ -280,6 +285,16 @@ void CWorldEditor::UpdateCursor()
} }
// ************ PRIVATE SLOTS ************ // ************ PRIVATE SLOTS ************
void CWorldEditor::OnPickModeEnter(QCursor Cursor)
{
ui->MainViewport->SetCursorState(Cursor);
}
void CWorldEditor::OnPickModeExit()
{
UpdateCursor();
}
void CWorldEditor::RefreshViewport() void CWorldEditor::RefreshViewport()
{ {
if (!mGizmo.IsTransforming()) if (!mGizmo.IsTransforming())

View File

@ -57,6 +57,8 @@ protected:
void UpdateCursor(); void UpdateCursor();
private slots: private slots:
void OnPickModeEnter(QCursor Cursor);
void OnPickModeExit();
void RefreshViewport(); void RefreshViewport();
void UpdateCameraOrbit(); void UpdateCameraOrbit();
void OnCameraSpeedChange(double speed); void OnCameraSpeedChange(double speed);