Added support for editing and resaving EGMC files + improved its preview rendering
This commit is contained in:
parent
c0b74c9883
commit
5c3a37ca4a
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -26,15 +28,17 @@ public:
|
||||||
// Setters
|
// Setters
|
||||||
void SetModel(CModel *pModel);
|
void SetModel(CModel *pModel);
|
||||||
|
|
||||||
inline void SetMatSet(u32 MatSet) { mActiveMatSet = MatSet; }
|
inline void SetMatSet(u32 MatSet) { mActiveMatSet = MatSet; }
|
||||||
inline void SetDynamicLighting(bool Enable) { mLightingEnabled = Enable; }
|
inline void SetDynamicLighting(bool Enable) { mLightingEnabled = Enable; }
|
||||||
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 CModel* Model() const { return mpModel; }
|
inline void SetScanOverlayEnabled(bool Enable) { mEnableScanOverlay = Enable; }
|
||||||
inline u32 MatSet() const { return mActiveMatSet; }
|
inline void SetScanOverlayColor(const CColor& rkColor) { mScanOverlayColor = rkColor; }
|
||||||
inline bool IsDynamicLightingEnabled() const { return mLightingEnabled; }
|
inline CModel* Model() const { return mpModel; }
|
||||||
inline u32 FindMeshID() const { return mpModel->GetSurface(0)->MeshID; }
|
inline u32 MatSet() const { return mActiveMatSet; }
|
||||||
|
inline bool IsDynamicLightingEnabled() const { return mLightingEnabled; }
|
||||||
|
inline u32 FindMeshID() const { return mpModel->GetSurface(0)->MeshID; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CMODELNODE_H
|
#endif // CMODELNODE_H
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,26 +21,36 @@ 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;
|
|
||||||
mPoiLookupMap[pPOI] = &mMaps.last();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NodeMap.contains(rkLink.MeshID))
|
for (auto it = pkMap->ModelIDs.begin(); it != pkMap->ModelIDs.end(); it++)
|
||||||
mPoiLookupMap[pPOI]->Models << NodeMap[rkLink.MeshID];
|
{
|
||||||
|
if (NodeMap.contains(*it))
|
||||||
|
*pModelList << NodeMap[*it];
|
||||||
|
}
|
||||||
|
|
||||||
|
mModelMap[pPoiNode] = pModelList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
{
|
{
|
||||||
if ( (Section == 0) && (Orientation == Qt::Horizontal) && (Role == Qt::DisplayRole) )
|
if ( (Section == 0) && (Orientation == Qt::Horizontal) && (Role == Qt::DisplayRole) )
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue