Moved selection handling code to CNodeSelection, implemented instance spawning, half-implemented instance deleting (this build is buggy/crash prone)

This commit is contained in:
parax0 2016-03-13 22:30:04 -06:00
parent b2699eb96f
commit f02f7ada0f
64 changed files with 1259 additions and 754 deletions

View File

@ -58,6 +58,8 @@ void Write(const TString& rkMessage)
fprintf(gpLogFile, "[%08.3f] %s\n", Time, *rkMessage); fprintf(gpLogFile, "[%08.3f] %s\n", Time, *rkMessage);
fflush(gpLogFile); fflush(gpLogFile);
} }
std::cout << rkMessage << "\n";
} }
void Error(const TString& rkMessage) void Error(const TString& rkMessage)
@ -65,7 +67,6 @@ void Error(const TString& rkMessage)
TString FullMessage = "ERROR: " + rkMessage; TString FullMessage = "ERROR: " + rkMessage;
Write(FullMessage); Write(FullMessage);
gErrorLog.push_back(FullMessage); gErrorLog.push_back(FullMessage);
std::cout << FullMessage << "\n";
} }
void Warning(const TString& rkMessage) void Warning(const TString& rkMessage)
@ -73,7 +74,6 @@ void Warning(const TString& rkMessage)
TString FullMessage = "Warning: " + rkMessage; TString FullMessage = "Warning: " + rkMessage;
Write(FullMessage); Write(FullMessage);
gErrorLog.push_back(FullMessage); gErrorLog.push_back(FullMessage);
std::cout << FullMessage << "\n";
} }
void FileWrite(const TString& rkFilename, const TString& rkMessage) void FileWrite(const TString& rkFilename, const TString& rkMessage)

View File

@ -15,6 +15,8 @@ public:
bool IsLayerEnabled() const; bool IsLayerEnabled() const;
bool IsSkyEnabled() const; bool IsSkyEnabled() const;
CModel* SkyModel() const; CModel* SkyModel() const;
inline CScriptObject* Instance() const { return mpObj; }
}; };
#endif // CAREAATTRIBUTES_H #endif // CAREAATTRIBUTES_H

View File

@ -2,23 +2,25 @@
#include "Core/Resource/Script/CScriptLayer.h" #include "Core/Resource/Script/CScriptLayer.h"
#include "Core/Render/CRenderer.h" #include "Core/Render/CRenderer.h"
CGameArea::CGameArea() : CResource() CGameArea::CGameArea()
: CResource()
, mWorldIndex(-1)
, mVertexCount(0)
, mTriangleCount(0)
, mTerrainMerged(false)
, mOriginalWorldMeshCount(0)
, mUsesCompression(false)
, mMaterialSet(nullptr)
, mpGeneratorLayer(nullptr)
, mpCollision(nullptr)
{ {
mVertexCount = 0;
mTriangleCount = 0;
mTerrainMerged = false;
mOriginalWorldMeshCount = 0;
mUsesCompression = false;
mMaterialSet = nullptr;
mpGeneratorLayer = nullptr;
mCollision = nullptr;
} }
CGameArea::~CGameArea() CGameArea::~CGameArea()
{ {
ClearTerrain(); ClearTerrain();
delete mCollision; delete mpCollision;
delete mpGeneratorLayer; delete mpGeneratorLayer;
for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++) for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++)
@ -108,85 +110,109 @@ void CGameArea::ClearScriptLayers()
mpGeneratorLayer = nullptr; mpGeneratorLayer = nullptr;
} }
EGame CGameArea::Version() u32 CGameArea::TotalInstanceCount() const
{ {
return mVersion; u32 Num = 0;
for (u32 iLyr = 0; iLyr < mScriptLayers.size(); iLyr++)
Num += mScriptLayers[iLyr]->NumInstances();
return Num;
} }
CTransform4f CGameArea::GetTransform() CScriptObject* CGameArea::InstanceByID(u32 InstanceID)
{
return mTransform;
}
u32 CGameArea::GetTerrainModelCount()
{
return mTerrainModels.size();
}
u32 CGameArea::GetStaticModelCount()
{
return mStaticTerrainModels.size();
}
CModel* CGameArea::GetTerrainModel(u32 mdl)
{
return mTerrainModels[mdl];
}
CStaticModel* CGameArea::GetStaticModel(u32 mdl)
{
return mStaticTerrainModels[mdl];
}
CCollisionMeshGroup* CGameArea::GetCollision()
{
return mCollision;
}
u32 CGameArea::GetScriptLayerCount()
{
return mScriptLayers.size();
}
CScriptLayer* CGameArea::GetScriptLayer(u32 index)
{
return mScriptLayers[index];
}
CScriptLayer* CGameArea::GetGeneratorLayer()
{
return mpGeneratorLayer;
}
CScriptObject* CGameArea::GetInstanceByID(u32 InstanceID)
{ {
auto it = mObjectMap.find(InstanceID); auto it = mObjectMap.find(InstanceID);
if (it != mObjectMap.end()) return it->second; if (it != mObjectMap.end()) return it->second;
else return nullptr; else return nullptr;
} }
u32 CGameArea::GetLightLayerCount()
CScriptObject* CGameArea::SpawnInstance(CScriptTemplate *pTemplate,
CScriptLayer *pLayer,
const CVector3f& rkPosition /*= CVector3f::skZero*/,
const CQuaternion& rkRotation /*= CQuaternion::skIdentity*/,
const CVector3f& rkScale /*= CVector3f::skOne*/,
u32 SuggestedID /*= -1*/,
u32 SuggestedLayerIndex /*= -1*/ )
{ {
return mLightLayers.size(); // Verify we can fit another instance in this area.
u32 NumInstances = TotalInstanceCount();
if (NumInstances >= 0xFFFF)
{
Log::Error("Unable to spawn a new script instance; too many instances in area (" + TString::FromInt32(NumInstances, 0, 10) + ")");
return nullptr;
}
// Check whether the suggested instance ID is valid
u32 InstanceID = SuggestedID;
if (InstanceID != -1)
{
if (mObjectMap.find(InstanceID) == mObjectMap.end())
InstanceID = -1;
}
// If not valid (or if there's no suggested ID) then determine a new instance ID
if (InstanceID == -1)
{
// Determine layer index
u32 LayerIndex = -1;
for (u32 iLyr = 0; iLyr < mScriptLayers.size(); iLyr++)
{
if (mScriptLayers[iLyr] == pLayer)
{
LayerIndex = iLyr;
break;
}
}
if (LayerIndex == -1)
{
Log::Error("Unable to spawn a new script instance; invalid script layer passed in");
return nullptr;
}
// Look for a valid instance ID
InstanceID = (LayerIndex << 26) | (mWorldIndex << 16) | 1;
while (true)
{
auto it = mObjectMap.find(InstanceID);
if (it == mObjectMap.end())
break;
else
InstanceID++;
}
}
// Spawn instance
CScriptObject *pInstance = new CScriptObject(InstanceID, this, pLayer, pTemplate);
pInstance->EvaluateProperties();
pInstance->SetPosition(rkPosition);
pInstance->SetRotation(rkRotation.ToEuler());
pInstance->SetScale(rkScale);
pInstance->SetName(pTemplate->Name());
if (pTemplate->Game() < eEchoesDemo) pInstance->SetActive(true);
pLayer->AddInstance(pInstance, SuggestedLayerIndex);
mObjectMap[InstanceID] = pInstance;
return pInstance;
} }
u32 CGameArea::GetLightCount(u32 layer) void CGameArea::DeleteInstance(CScriptObject *pInstance)
{ {
if (mLightLayers.empty()) return 0; pInstance->Layer()->RemoveInstance(pInstance);
return mLightLayers[layer].size(); pInstance->Template()->RemoveObject(pInstance);
}
CLight* CGameArea::GetLight(u32 layer, u32 light) auto it = mObjectMap.find(pInstance->InstanceID());
{ if (it != mObjectMap.end()) mObjectMap.erase(it);
return mLightLayers[layer][light];
}
CPoiToWorld* CGameArea::GetPoiToWorldMap() if (mpPoiToWorldMap && mpPoiToWorldMap->HasPoiMappings(pInstance->InstanceID()))
{ mpPoiToWorldMap->RemovePoi(pInstance->InstanceID());
return mpPoiToWorldMap;
}
CAABox CGameArea::AABox() pInstance->BreakAllLinks();
{ delete pInstance;
return mAABox;
} }

View File

@ -9,12 +9,14 @@
#include "Core/Resource/Model/CModel.h" #include "Core/Resource/Model/CModel.h"
#include "Core/Resource/Model/CStaticModel.h" #include "Core/Resource/Model/CStaticModel.h"
#include <Common/types.h> #include <Common/types.h>
#include <Math/CQuaternion.h>
#include <Math/CTransform4f.h> #include <Math/CTransform4f.h>
#include <unordered_map> #include <unordered_map>
class CScriptLayer; class CScriptLayer;
class CScriptObject; class CScriptObject;
class CScriptTemplate;
class CGameArea : public CResource class CGameArea : public CResource
{ {
@ -23,6 +25,7 @@ class CGameArea : public CResource
friend class CAreaCooker; friend class CAreaCooker;
EGame mVersion; EGame mVersion;
u32 mWorldIndex;
u32 mVertexCount; u32 mVertexCount;
u32 mTriangleCount; u32 mTriangleCount;
bool mTerrainMerged; bool mTerrainMerged;
@ -50,7 +53,7 @@ class CGameArea : public CResource
CScriptLayer *mpGeneratorLayer; CScriptLayer *mpGeneratorLayer;
std::unordered_map<u32, CScriptObject*> mObjectMap; std::unordered_map<u32, CScriptObject*> mObjectMap;
// Collision // Collision
CCollisionMeshGroup *mCollision; CCollisionMeshGroup *mpCollision;
// Lights // Lights
std::vector<std::vector<CLight*>> mLightLayers; std::vector<std::vector<CLight*>> mLightLayers;
// Object to Static Geometry Map // Object to Static Geometry Map
@ -64,24 +67,34 @@ public:
void MergeTerrain(); void MergeTerrain();
void ClearTerrain(); void ClearTerrain();
void ClearScriptLayers(); void ClearScriptLayers();
u32 TotalInstanceCount() const;
CScriptObject* InstanceByID(u32 InstanceID);
CScriptObject* SpawnInstance(CScriptTemplate *pTemplate, CScriptLayer *pLayer,
const CVector3f& rkPosition = CVector3f::skZero,
const CQuaternion& rkRotation = CQuaternion::skIdentity,
const CVector3f& rkScale = CVector3f::skOne,
u32 SuggestedID = -1, u32 SuggestedLayerIndex = -1);
void DeleteInstance(CScriptObject *pInstance);
// Getters // Inline Accessors
EGame Version(); inline EGame Version() const { return mVersion; }
CTransform4f GetTransform(); inline u32 WorldIndex() const { return mWorldIndex; }
u32 GetTerrainModelCount(); inline CTransform4f GetTransform() const { return mTransform; }
u32 GetStaticModelCount(); inline u32 GetTerrainModelCount() const { return mTerrainModels.size(); }
CModel* GetTerrainModel(u32 mdl); inline u32 GetStaticModelCount() const { return mStaticTerrainModels.size(); }
CStaticModel* GetStaticModel(u32 mdl); inline CModel* GetTerrainModel(u32 iMdl) const { return mTerrainModels[iMdl]; }
CCollisionMeshGroup* GetCollision(); inline CStaticModel* GetStaticModel(u32 iMdl) const { return mStaticTerrainModels[iMdl]; }
u32 GetScriptLayerCount(); inline CCollisionMeshGroup* GetCollision() const { return mpCollision; }
CScriptLayer* GetScriptLayer(u32 index); inline u32 GetScriptLayerCount() const { return mScriptLayers.size(); }
CScriptLayer* GetGeneratorLayer(); inline CScriptLayer* GetScriptLayer(u32 Index) const { return mScriptLayers[Index]; }
CScriptObject* GetInstanceByID(u32 InstanceID); inline CScriptLayer* GetGeneratorLayer() const { return mpGeneratorLayer; }
u32 GetLightLayerCount(); inline u32 GetLightLayerCount() const { return mLightLayers.size(); }
u32 GetLightCount(u32 layer); inline u32 GetLightCount(u32 LayerIndex) const { return (LayerIndex < mLightLayers.size() ? mLightLayers[LayerIndex].size() : 0); }
CLight* GetLight(u32 layer, u32 light); inline CLight* GetLight(u32 LayerIndex, u32 LightIndex) const { return mLightLayers[LayerIndex][LightIndex]; }
CPoiToWorld* GetPoiToWorldMap(); inline CPoiToWorld* GetPoiToWorldMap() const { return mpPoiToWorldMap; }
CAABox AABox(); inline CAABox AABox() const { return mAABox; }
inline void SetWorldIndex(u32 NewWorldIndex) { mWorldIndex = NewWorldIndex; }
}; };
#endif // CGAMEAREA_H #endif // CGAMEAREA_H

View File

@ -39,6 +39,22 @@ public:
{ {
return mMaps[Index]; return mMaps[Index];
} }
inline const SPoiMap* MapByID(u32 InstanceID) const
{
auto it = mPoiLookupMap.find(InstanceID);
if (it != mPoiLookupMap.end())
return it->second;
else
return nullptr;
}
bool HasPoiMappings(u32 InstanceID) const
{
auto it = mPoiLookupMap.find(InstanceID);
return (it != mPoiLookupMap.end());
}
}; };
#endif // CPOITOWORLD_H #endif // CPOITOWORLD_H

View File

@ -31,7 +31,7 @@ public:
inline CUniqueID ID() const inline CUniqueID ID() const
{ {
TString FileName = mPath.GetFileName(); TString FileName = mPath.GetFileName(false);
if (!mIsPath) if (!mIsPath)
return CUniqueID::FromString(FileName); return CUniqueID::FromString(FileName);

View File

@ -16,12 +16,12 @@ CWorld::~CWorld()
{ {
} }
void CWorld::SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex) void CWorld::SetAreaLayerInfo(CGameArea *pArea)
{ {
// The AreaIndex parameter is a placeholder until an improved world loader is implemented. // The AreaIndex parameter is a placeholder until an improved world loader is implemented.
// For now it's the easiest/fastest way to do this because this function is called from // For now it's the easiest/fastest way to do this because this function is called from
// the start window and the start window already knows the area index. // the start window and the start window already knows the area index.
SArea& AreaInfo = mAreas[AreaIndex]; SArea& AreaInfo = mAreas[pArea->WorldIndex()];
for (u32 iLyr = 0; iLyr < pArea->GetScriptLayerCount(); iLyr++) for (u32 iLyr = 0; iLyr < pArea->GetScriptLayerCount(); iLyr++)
{ {

View File

@ -84,7 +84,7 @@ public:
CWorld(); CWorld();
~CWorld(); ~CWorld();
void SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex); void SetAreaLayerInfo(CGameArea *pArea);
// Setters // Setters
EGame Version(); EGame Version();

View File

@ -595,7 +595,7 @@ void CAreaLoader::ReadCollision()
{ {
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)"); Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
mpSectionMgr->ToSection(mCollisionBlockNum); mpSectionMgr->ToSection(mCollisionBlockNum);
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA); mpArea->mpCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
} }
void CAreaLoader::ReadEGMC() void CAreaLoader::ReadEGMC()
@ -643,7 +643,7 @@ void CAreaLoader::SetUpObjects()
if (iConMap != mConnectionMap.end()) if (iConMap != mConnectionMap.end())
{ {
CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID); CScriptObject *pObj = mpArea->InstanceByID(InstanceID);
pObj->mInLinks = iConMap->second; pObj->mInLinks = iConMap->second;
} }
} }

View File

@ -214,8 +214,8 @@ CScriptObject* CScriptLoader::LoadObjectMP1(IInputStream& SCLY)
return nullptr; return nullptr;
} }
mpObj = new CScriptObject(mpArea, mpLayer, pTemp); u32 InstanceID = SCLY.ReadLong();
mpObj->mInstanceID = SCLY.ReadLong(); mpObj = new CScriptObject(InstanceID, mpArea, mpLayer, pTemp);
// Load connections // Load connections
u32 NumLinks = SCLY.ReadLong(); u32 NumLinks = SCLY.ReadLong();
@ -249,7 +249,7 @@ CScriptLayer* CScriptLoader::LoadLayerMP1(IInputStream &SCLY)
SCLY.Seek(0x1, SEEK_CUR); // One unknown byte at the start of each layer SCLY.Seek(0x1, SEEK_CUR); // One unknown byte at the start of each layer
u32 NumObjects = SCLY.ReadLong(); u32 NumObjects = SCLY.ReadLong();
mpLayer = new CScriptLayer(); mpLayer = new CScriptLayer(mpArea);
mpLayer->Reserve(NumObjects); mpLayer->Reserve(NumObjects);
for (u32 iObj = 0; iObj < NumObjects; iObj++) for (u32 iObj = 0; iObj < NumObjects; iObj++)
@ -327,8 +327,8 @@ CScriptObject* CScriptLoader::LoadObjectMP2(IInputStream& SCLY)
return nullptr; return nullptr;
} }
mpObj = new CScriptObject(mpArea, mpLayer, pTemplate); u32 InstanceID = SCLY.ReadLong();
mpObj->mInstanceID = SCLY.ReadLong(); mpObj = new CScriptObject(InstanceID, mpArea, mpLayer, pTemplate);
// Load connections // Load connections
u32 NumConnections = SCLY.ReadShort(); u32 NumConnections = SCLY.ReadShort();
@ -359,7 +359,7 @@ CScriptLayer* CScriptLoader::LoadLayerMP2(IInputStream& SCLY)
SCLY.Seek(0x1, SEEK_CUR); // Skipping version. todo: verify this? SCLY.Seek(0x1, SEEK_CUR); // Skipping version. todo: verify this?
u32 NumObjects = SCLY.ReadLong(); u32 NumObjects = SCLY.ReadLong();
mpLayer = new CScriptLayer(); mpLayer = new CScriptLayer(mpArea);
mpLayer->Reserve(NumObjects); mpLayer->Reserve(NumObjects);
for (u32 iObj = 0; iObj < NumObjects; iObj++) for (u32 iObj = 0; iObj < NumObjects; iObj++)
@ -372,9 +372,10 @@ CScriptLayer* CScriptLoader::LoadLayerMP2(IInputStream& SCLY)
return mpLayer; return mpLayer;
} }
CScriptLayer* CScriptLoader::LoadLayer(IInputStream &SCLY, CGameArea *pArea, EGame Version) // ************ STATIC ************
CScriptLayer* CScriptLoader::LoadLayer(IInputStream& rSCLY, CGameArea *pArea, EGame Version)
{ {
if (!SCLY.IsValid()) return nullptr; if (!rSCLY.IsValid()) return nullptr;
CScriptLoader Loader; CScriptLoader Loader;
Loader.mVersion = Version; Loader.mVersion = Version;
@ -391,7 +392,7 @@ CScriptLayer* CScriptLoader::LoadLayer(IInputStream &SCLY, CGameArea *pArea, EGa
CTemplateLoader::LoadGameTemplates(Version); CTemplateLoader::LoadGameTemplates(Version);
if (Version <= ePrime) if (Version <= ePrime)
return Loader.LoadLayerMP1(SCLY); return Loader.LoadLayerMP1(rSCLY);
else else
return Loader.LoadLayerMP2(SCLY); return Loader.LoadLayerMP2(rSCLY);
} }

View File

@ -52,8 +52,8 @@ public:
void SetSender(u32 NewSenderID, u32 Index = -1) void SetSender(u32 NewSenderID, u32 Index = -1)
{ {
u32 OldSenderID = mSenderID; u32 OldSenderID = mSenderID;
CScriptObject *pOldSender = mpArea->GetInstanceByID(OldSenderID); CScriptObject *pOldSender = mpArea->InstanceByID(OldSenderID);
CScriptObject *pNewSender = mpArea->GetInstanceByID(NewSenderID); CScriptObject *pNewSender = mpArea->InstanceByID(NewSenderID);
mSenderID = NewSenderID; mSenderID = NewSenderID;
pOldSender->RemoveLink(eOutgoing, this); pOldSender->RemoveLink(eOutgoing, this);
@ -63,8 +63,8 @@ public:
void SetReceiver(u32 NewReceiverID, u32 Index = -1) void SetReceiver(u32 NewReceiverID, u32 Index = -1)
{ {
u32 OldReceiverID = mSenderID; u32 OldReceiverID = mSenderID;
CScriptObject *pOldReceiver = mpArea->GetInstanceByID(OldReceiverID); CScriptObject *pOldReceiver = mpArea->InstanceByID(OldReceiverID);
CScriptObject *pNewReceiver = mpArea->GetInstanceByID(NewReceiverID); CScriptObject *pNewReceiver = mpArea->InstanceByID(NewReceiverID);
mReceiverID = NewReceiverID; mReceiverID = NewReceiverID;
pOldReceiver->RemoveLink(eIncoming, this); pOldReceiver->RemoveLink(eIncoming, this);
@ -73,7 +73,7 @@ public:
u32 SenderIndex() const u32 SenderIndex() const
{ {
CScriptObject *pSender = mpArea->GetInstanceByID(mSenderID); CScriptObject *pSender = mpArea->InstanceByID(mSenderID);
for (u32 iLink = 0; iLink < pSender->NumLinks(eOutgoing); iLink++) for (u32 iLink = 0; iLink < pSender->NumLinks(eOutgoing); iLink++)
{ {
@ -86,7 +86,7 @@ public:
u32 ReceiverIndex() const u32 ReceiverIndex() const
{ {
CScriptObject *pReceiver = mpArea->GetInstanceByID(mReceiverID); CScriptObject *pReceiver = mpArea->InstanceByID(mReceiverID);
for (u32 iLink = 0; iLink < pReceiver->NumLinks(eIncoming); iLink++) for (u32 iLink = 0; iLink < pReceiver->NumLinks(eIncoming); iLink++)
{ {
@ -117,8 +117,8 @@ public:
u32 Message() const { return mMessageID; } u32 Message() const { return mMessageID; }
u32 SenderID() const { return mSenderID; } u32 SenderID() const { return mSenderID; }
u32 ReceiverID() const { return mReceiverID; } u32 ReceiverID() const { return mReceiverID; }
CScriptObject* Sender() const { return mpArea->GetInstanceByID(mSenderID); } CScriptObject* Sender() const { return mpArea->InstanceByID(mSenderID); }
CScriptObject* Receiver() const { return mpArea->GetInstanceByID(mReceiverID); } CScriptObject* Receiver() const { return mpArea->InstanceByID(mReceiverID); }
void SetState(u32 StateID) { mStateID = StateID; } void SetState(u32 StateID) { mStateID = StateID; }
void SetMessage(u32 MessageID) { mMessageID = MessageID; } void SetMessage(u32 MessageID) { mMessageID = MessageID; }

View File

@ -8,13 +8,15 @@
class CScriptLayer class CScriptLayer
{ {
CGameArea *mpArea;
TString mLayerName; TString mLayerName;
bool mActive; bool mActive;
bool mVisible; bool mVisible;
std::vector<CScriptObject*> mInstances; std::vector<CScriptObject*> mInstances;
public: public:
CScriptLayer() CScriptLayer(CGameArea *pArea)
: mLayerName("New Layer") : mpArea(pArea)
, mLayerName("New Layer")
, mActive(true) , mActive(true)
, mVisible(true) , mVisible(true)
{ {
@ -27,8 +29,16 @@ public:
} }
// Data Manipulation // Data Manipulation
void AddInstance(CScriptObject *pObject) void AddInstance(CScriptObject *pObject, u32 Index = -1)
{ {
if (Index != -1 && Index < mInstances.size())
{
auto it = mInstances.begin();
std::advance(it, Index);
mInstances.insert(it, pObject);
}
else
mInstances.push_back(pObject); mInstances.push_back(pObject);
} }
@ -67,6 +77,7 @@ public:
} }
// Accessors // Accessors
inline CGameArea* Area() const { return mpArea; }
inline TString Name() const { return mLayerName; } inline TString Name() const { return mLayerName; }
inline bool IsActive() const { return mActive; } inline bool IsActive() const { return mActive; }
inline bool IsVisible() const { return mVisible; } inline bool IsVisible() const { return mVisible; }
@ -88,6 +99,17 @@ public:
inline void SetActive(bool Active) { mActive = Active; } inline void SetActive(bool Active) { mActive = Active; }
inline void SetVisible(bool Visible) { mVisible = Visible; } inline void SetVisible(bool Visible) { mVisible = Visible; }
inline u32 AreaIndex() const
{
for (u32 iLyr = 0; iLyr < mpArea->GetScriptLayerCount(); iLyr++)
{
if (mpArea->GetScriptLayer(iLyr) == this)
return iLyr;
}
return -1;
}
// Operators // Operators
CScriptObject* operator[](u32 Index) { return InstanceByIndex(Index); } CScriptObject* operator[](u32 Index) { return InstanceByIndex(Index); }
}; };

View File

@ -3,11 +3,12 @@
#include "CMasterTemplate.h" #include "CMasterTemplate.h"
#include "Core/Resource/CAnimSet.h" #include "Core/Resource/CAnimSet.h"
CScriptObject::CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate) CScriptObject::CScriptObject(u32 InstanceID, CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate)
: mpTemplate(pTemplate) : mpTemplate(pTemplate)
, mpArea(pArea) , mpArea(pArea)
, mpLayer(pLayer) , mpLayer(pLayer)
, mVersion(0) , mVersion(0)
, mInstanceID(InstanceID)
, mpDisplayModel(nullptr) , mpDisplayModel(nullptr)
, mpCollision(nullptr) , mpCollision(nullptr)
, mHasInGameModel(false) , mHasInGameModel(false)
@ -75,16 +76,27 @@ bool CScriptObject::IsEditorProperty(IProperty *pProp)
); );
} }
void CScriptObject::SetLayer(CScriptLayer *pLayer) void CScriptObject::SetLayer(CScriptLayer *pLayer, u32 NewLayerIndex)
{ {
if (pLayer != mpLayer) if (pLayer != mpLayer)
{ {
mpLayer->RemoveInstance(this); mpLayer->RemoveInstance(this);
mpLayer = pLayer; mpLayer = pLayer;
mpLayer->AddInstance(this); mpLayer->AddInstance(this, NewLayerIndex);
} }
} }
u32 CScriptObject::LayerIndex() const
{
for (u32 iInst = 0; iInst < mpLayer->NumInstances(); iInst++)
{
if (mpLayer->InstanceByIndex(iInst) == this)
return iInst;
}
return -1;
}
bool CScriptObject::HasNearVisibleActivation() const bool CScriptObject::HasNearVisibleActivation() const
{ {
/* This function is used to check whether an inactive DKCR object should render in game mode. DKCR deactivates a lot of /* This function is used to check whether an inactive DKCR object should render in game mode. DKCR deactivates a lot of
@ -240,6 +252,26 @@ void CScriptObject::RemoveLink(ELinkType Type, CLink *pLink)
} }
} }
void CScriptObject::BreakAllLinks()
{
for (auto it = mInLinks.begin(); it != mInLinks.end(); it++)
{
CLink *pLink = *it;
pLink->Sender()->RemoveLink(eOutgoing, pLink);
delete pLink;
}
for (auto it = mOutLinks.begin(); it != mOutLinks.end(); it++)
{
CLink *pLink = *it;
pLink->Receiver()->RemoveLink(eIncoming, pLink);
delete pLink;
}
mInLinks.clear();
mOutLinks.clear();
}
TString CScriptObject::InstanceName() const TString CScriptObject::InstanceName() const
{ {
if (mpInstanceName) if (mpInstanceName)

View File

@ -50,7 +50,7 @@ class CScriptObject
mutable bool mIsCheckingNearVisibleActivation; mutable bool mIsCheckingNearVisibleActivation;
public: public:
CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate); CScriptObject(u32 InstanceID, CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate);
~CScriptObject(); ~CScriptObject();
void EvaluateProperties(); void EvaluateProperties();
@ -59,7 +59,8 @@ public:
void EvaluateCollisionModel(); void EvaluateCollisionModel();
void EvaluateVolume(); void EvaluateVolume();
bool IsEditorProperty(IProperty *pProp); bool IsEditorProperty(IProperty *pProp);
void SetLayer(CScriptLayer *pLayer); void SetLayer(CScriptLayer *pLayer, u32 NewLayerIndex = -1);
u32 LayerIndex() const;
bool HasNearVisibleActivation() const; bool HasNearVisibleActivation() const;
CScriptTemplate* Template() const; CScriptTemplate* Template() const;
@ -78,6 +79,7 @@ public:
CLink* Link(ELinkType Type, u32 Index) const; CLink* Link(ELinkType Type, u32 Index) const;
void AddLink(ELinkType Type, CLink *pLink, u32 Index = -1); void AddLink(ELinkType Type, CLink *pLink, u32 Index = -1);
void RemoveLink(ELinkType Type, CLink *pLink); void RemoveLink(ELinkType Type, CLink *pLink);
void BreakAllLinks();
CVector3f Position() const; CVector3f Position() const;
CVector3f Rotation() const; CVector3f Rotation() const;

View File

@ -65,6 +65,18 @@ CScriptNode* CScene::CreateScriptNode(CScriptObject *pObj)
CScriptNode *pNode = new CScriptNode(this, mpAreaRootNode, pObj); CScriptNode *pNode = new CScriptNode(this, mpAreaRootNode, pObj);
mNodes[eScriptNode].push_back(pNode); mNodes[eScriptNode].push_back(pNode);
mScriptNodeMap[pObj->InstanceID()] = pNode;
pNode->BuildLightList(mpArea);
// AreaAttributes check
switch (pObj->ObjectTypeID())
{
case 0x4E: // MP1 AreaAttributes ID
case 0x52454141: // MP2/MP3/DKCR AreaAttributes ID ("REAA")
mAreaAttributesObjects.emplace_back( CAreaAttributes(pObj) );
break;
}
mNumNodes++; mNumNodes++;
return pNode; return pNode;
} }
@ -79,6 +91,48 @@ CLightNode* CScene::CreateLightNode(CLight *pLight)
return pNode; return pNode;
} }
void CScene::DeleteNode(CSceneNode *pNode)
{
ENodeType Type = pNode->NodeType();
for (auto it = mNodes[Type].begin(); it != mNodes[Type].end(); it++)
{
if (*it == pNode)
{
mNodes[Type].erase(it);
break;
}
}
if (Type == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
auto ScriptMapIt = mScriptNodeMap.find(pScript->Object()->InstanceID());
if (ScriptMapIt != mScriptNodeMap.end())
mScriptNodeMap.erase(ScriptMapIt);
switch (pScript->Object()->ObjectTypeID())
{
case 0x4E:
case 0x52454141:
for (auto it = mAreaAttributesObjects.begin(); it != mAreaAttributesObjects.end(); it++)
{
if ((*it).Instance() == pScript->Object())
{
mAreaAttributesObjects.erase(it);
break;
}
}
break;
}
}
pNode->Unparent();
delete pNode;
mNumNodes--;
}
void CScene::SetActiveArea(CGameArea *pArea) void CScene::SetActiveArea(CGameArea *pArea)
{ {
// Clear existing area // Clear existing area
@ -121,20 +175,7 @@ void CScene::SetActiveArea(CGameArea *pArea)
for (u32 iObj = 0; iObj < NumObjects; iObj++) for (u32 iObj = 0; iObj < NumObjects; iObj++)
{ {
CScriptObject *pObj = pLayer->InstanceByIndex(iObj); CScriptObject *pObj = pLayer->InstanceByIndex(iObj);
CScriptNode *pNode = CreateScriptNode(pObj); CreateScriptNode(pObj);
pNode->BuildLightList(mpArea);
// Add to map
mScriptNodeMap[pObj->InstanceID()] = pNode;
// AreaAttributes check
switch (pObj->ObjectTypeID())
{
case 0x4E: // MP1 AreaAttributes ID
case 0x52454141: // MP2/MP3/DKCR AreaAttributes ID ("REAA")
mAreaAttributesObjects.emplace_back( CAreaAttributes(pObj) );
break;
}
} }
} }
@ -144,10 +185,7 @@ void CScene::SetActiveArea(CGameArea *pArea)
for (u32 iObj = 0; iObj < pGenLayer->NumInstances(); iObj++) for (u32 iObj = 0; iObj < pGenLayer->NumInstances(); iObj++)
{ {
CScriptObject *pObj = pGenLayer->InstanceByIndex(iObj); CScriptObject *pObj = pGenLayer->InstanceByIndex(iObj);
CScriptNode *pNode = CreateScriptNode(pObj); CreateScriptNode(pObj);
// Add to map
mScriptNodeMap[pObj->InstanceID()] = pNode;
} }
} }
@ -217,16 +255,10 @@ void CScene::AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo)
FShowFlags ShowFlags = (ViewInfo.GameMode ? gkGameModeShowFlags : ViewInfo.ShowFlags); FShowFlags ShowFlags = (ViewInfo.GameMode ? gkGameModeShowFlags : ViewInfo.ShowFlags);
FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags); FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags);
for (auto it = mNodes.begin(); it != mNodes.end(); it++) for (CSceneIterator It(this, NodeFlags, false); It; ++It)
{ {
if (NodeFlags & it->first) if (ViewInfo.GameMode || It->IsVisible())
{ It->AddToRenderer(pRenderer, ViewInfo);
std::vector<CSceneNode*>& rNodeVec = it->second;
for (u32 iNode = 0; iNode < rNodeVec.size(); iNode++)
if (ViewInfo.GameMode || rNodeVec[iNode]->IsVisible())
rNodeVec[iNode]->AddToRenderer(pRenderer, ViewInfo);
}
} }
} }
@ -236,16 +268,10 @@ SRayIntersection CScene::SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo
FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags); FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags);
CRayCollisionTester Tester(Ray); CRayCollisionTester Tester(Ray);
for (auto it = mNodes.begin(); it != mNodes.end(); it++) for (CSceneIterator It(this, NodeFlags, false); It; ++It)
{ {
if (NodeFlags & it->first) if (It->IsVisible())
{ It->RayAABoxIntersectTest(Tester, ViewInfo);
std::vector<CSceneNode*>& rNodeVec = it->second;
for (u32 iNode = 0; iNode < rNodeVec.size(); iNode++)
if (rNodeVec[iNode]->IsVisible())
rNodeVec[iNode]->RayAABoxIntersectTest(Tester, ViewInfo);
}
} }
return Tester.TestNodes(ViewInfo); return Tester.TestNodes(ViewInfo);

View File

@ -51,6 +51,7 @@ public:
CCollisionNode* CreateCollisionNode(CCollisionMeshGroup *pMesh); CCollisionNode* CreateCollisionNode(CCollisionMeshGroup *pMesh);
CScriptNode* CreateScriptNode(CScriptObject *pObj); CScriptNode* CreateScriptNode(CScriptObject *pObj);
CLightNode* CreateLightNode(CLight *pLight); CLightNode* CreateLightNode(CLight *pLight);
void DeleteNode(CSceneNode *pNode);
void SetActiveArea(CGameArea *pArea); void SetActiveArea(CGameArea *pArea);
void SetActiveWorld(CWorld *pWorld); void SetActiveWorld(CWorld *pWorld);
void PostLoad(); void PostLoad();

View File

@ -38,7 +38,7 @@ public:
inline operator bool() const inline operator bool() const
{ {
return DoneIterating(); return !DoneIterating();
} }
inline CSceneNode* operator*() const inline CSceneNode* operator*() const

View File

@ -1,58 +0,0 @@
#include "CNodeSelection.h"
CSceneSelection::CSceneSelection(CScene *pScene)
{
mpScene = pScene;
}
void CSceneSelection::SelectNode(CSceneNode *pNode)
{
// There shouldn't be more than one selection per scene, so this should be safe.
if (!pNode->IsSelected())
{
pNode->SetSelected(true);
mSelectedNodes.push_back(pNode);
}
}
void CSceneSelection::DeselectNode(CSceneNode *pNode)
{
if (pNode->IsSelected())
{
pNode->SetSelected(false);
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
{
if (*it == pNode)
{
mSelectedNodes.erase(it);
break;
}
}
}
}
u32 CSceneSelection::SelectionSize()
{
return mSelectedNodes.size();
}
CSceneNode* CSceneSelection::NodeByIndex(u32 Index)
{
if (Index >= SelectionSize()) return nullptr;
return mSelectedNodes[Index];
}
void CSceneSelection::ClearSelection()
{
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
(*it)->SetSelected(false);
mSelectedNodes.clear();
}
// ************ OPERATORS ************
CSceneNode* CSceneSelection::operator[](u32 Index)
{
return NodeByIndex(Index);
}

View File

@ -1,24 +1,126 @@
#ifndef CNODESELECTION_H #ifndef CNODESELECTION_H
#define CNODESELECTION_H #define CNODESELECTION_H
#include <Core/Scene/CSceneIterator.h>
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <list> #include <Core/Scene/CScriptNode.h>
#include <QList>
#include <QObject>
class CSceneSelection class CNodeSelection : public QObject
{ {
CScene *mpScene; Q_OBJECT
std::vector<CSceneNode*> mSelectedNodes;
FNodeFlags mAllowedNodes;
QList<CSceneNode*> mSelectedNodes;
mutable CAABox mCachedBounds;
mutable bool mBoundsDirty;
public: public:
CSceneSelection(CScene *pScene); CNodeSelection()
void SelectNode(CSceneNode *pNode); : mAllowedNodes(eAllNodeTypes)
void DeselectNode(CSceneNode *pNode); , mBoundsDirty(true) {}
u32 SelectionSize();
CSceneNode* NodeByIndex(u32 Index);
void ClearSelection();
// Operators ~CNodeSelection()
CSceneNode* operator[](u32 Index); {
foreach (CSceneNode *pNode, mSelectedNodes)
pNode->SetSelected(false);
}
void SelectNode(CSceneNode *pNode)
{
if (IsAllowedType(pNode->NodeType()) && !pNode->IsSelected())
{
pNode->SetSelected(true);
mSelectedNodes << pNode;
mCachedBounds.ExpandBounds(pNode->AABox());
emit Modified();
}
}
void DeselectNode(CSceneNode *pNode)
{
if (pNode->IsSelected())
{
pNode->SetSelected(false);
mSelectedNodes.removeOne(pNode);
mBoundsDirty = true;
emit Modified();
}
}
void Clear()
{
foreach (CSceneNode *pNode, mSelectedNodes)
pNode->SetSelected(false);
mSelectedNodes.clear();
mBoundsDirty = true;
emit Modified();
}
void ClearAndSelectNode(CSceneNode *pNode)
{
// Block signals for Clear so that Modified only emits once.
blockSignals(true);
Clear();
blockSignals(false);
SelectNode(pNode);
}
void SetSelectedNodes(const QList<CSceneNode*>& rkList)
{
blockSignals(true);
Clear();
foreach (CSceneNode *pNode, rkList)
SelectNode(pNode);
blockSignals(false);
mBoundsDirty = true;
emit Modified();
}
CAABox Bounds() const
{
if (mBoundsDirty)
{
mCachedBounds = CAABox::skInfinite;
foreach (CSceneNode *pNode, mSelectedNodes)
{
mCachedBounds.ExpandBounds(pNode->AABox());
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
if (pScript->HasPreviewVolume())
mCachedBounds.ExpandBounds(pScript->PreviewVolumeAABox());
}
}
mBoundsDirty = false;
}
return mCachedBounds;
}
inline u32 Size() const { return mSelectedNodes.size(); }
inline bool IsEmpty() const { return Size() == 0; }
inline CSceneNode* At(u32 Index) const { return mSelectedNodes[Index]; }
inline CSceneNode* Front() const { return mSelectedNodes.front(); }
inline CSceneNode* Back() const { return mSelectedNodes.back(); }
inline CSceneNode* operator[](u32 Index) const { return mSelectedNodes[Index]; }
inline void UpdateBounds() { mBoundsDirty = true; }
inline void SetAllowedNodeTypes(FNodeFlags Types) { mAllowedNodes = Types; }
inline bool IsAllowedType(ENodeType Type) const { return (mAllowedNodes & Type) != 0; }
inline bool IsAllowedType(CSceneNode *pNode) const { return (mAllowedNodes & pNode->NodeType()) != 0; }
inline QList<CSceneNode*> SelectedNodeList() const { return mSelectedNodes; }
signals:
void Modified();
}; };
#endif // CNODESELECTION_H #endif // CNODESELECTION_H

View File

@ -1,4 +1,5 @@
#include "CSceneViewport.h" #include "CSceneViewport.h"
#include "CSelectionIterator.h"
#include "UICommon.h" #include "UICommon.h"
#include "Editor/Undo/UndoCommands.h" #include "Editor/Undo/UndoCommands.h"
#include <Core/Render/CDrawUtil.h> #include <Core/Render/CDrawUtil.h>
@ -386,10 +387,8 @@ void CSceneViewport::OnToggleSelect()
void CSceneViewport::OnHideSelection() void CSceneViewport::OnHideSelection()
{ {
const QList<CSceneNode*>& rkSelection = mpEditor->GetSelection(); for (CSelectionIterator It(mpEditor->Selection()); It; ++It)
It->SetVisible(false);
foreach (CSceneNode *pNode, rkSelection)
pNode->SetVisible(false);
} }
void CSceneViewport::OnHideUnselected() void CSceneViewport::OnHideUnselected()

View File

@ -0,0 +1,25 @@
#ifndef CSELECTIONITERATOR
#define CSELECTIONITERATOR
#include "CNodeSelection.h"
class CSelectionIterator
{
CNodeSelection *mpSelection;
u32 mCurrentIndex;
public:
CSelectionIterator(CNodeSelection *pSelection)
: mpSelection(pSelection), mCurrentIndex(0) {}
inline void Next() { mCurrentIndex++; }
inline bool DoneIterating() const { return (mCurrentIndex == mpSelection->Size()); }
inline operator bool() const { return !DoneIterating(); }
inline CSceneNode* operator*() const { return mpSelection->At(mCurrentIndex); }
inline CSceneNode* operator->() const { return mpSelection->At(mCurrentIndex); }
inline CSelectionIterator& operator++() { Next(); return *this; }
inline CSelectionIterator operator++(int) { CSelectionIterator Copy = *this; Next(); return Copy; }
};
#endif // CSELECTIONITERATOR

View File

@ -183,8 +183,9 @@ void CStartWindow::on_LaunchWorldEditorButton_clicked()
else else
{ {
mpWorld->SetAreaLayerInfo(pArea, mSelectedAreaIndex); pArea->SetWorldIndex(mSelectedAreaIndex);
mpWorldEditor->SetArea(mpWorld, pArea, mSelectedAreaIndex); mpWorld->SetAreaLayerInfo(pArea);
mpWorldEditor->SetArea(mpWorld, pArea);
gResCache.Clean(); gResCache.Clean();
mpWorldEditor->setWindowModality(Qt::WindowModal); mpWorldEditor->setWindowModality(Qt::WindowModal);

View File

@ -149,17 +149,19 @@ HEADERS += \
Undo/CAddLinkCommand.h \ Undo/CAddLinkCommand.h \
Undo/CDeleteLinksCommand.h \ Undo/CDeleteLinksCommand.h \
Undo/CEditLinkCommand.h \ Undo/CEditLinkCommand.h \
WorldEditor/CConfirmUnlinkDialog.h WorldEditor/CConfirmUnlinkDialog.h \
Undo/CDeleteSelectionCommand.h \
Undo/CCreateInstanceCommand.h \
WorldEditor/CTemplateMimeData.h \
WorldEditor/CTemplateListView.h \
CSelectionIterator.h
# Source Files # Source Files
SOURCES += \ SOURCES += \
ModelEditor/CModelEditorViewport.cpp \ ModelEditor/CModelEditorViewport.cpp \
ModelEditor/CModelEditorWindow.cpp \ ModelEditor/CModelEditorWindow.cpp \
Undo/CClearSelectionCommand.cpp \
Undo/CDeselectNodeCommand.cpp \
Undo/CRotateNodeCommand.cpp \ Undo/CRotateNodeCommand.cpp \
Undo/CScaleNodeCommand.cpp \ Undo/CScaleNodeCommand.cpp \
Undo/CSelectNodeCommand.cpp \
Undo/CTranslateNodeCommand.cpp \ Undo/CTranslateNodeCommand.cpp \
Widgets/IPreviewPanel.cpp \ Widgets/IPreviewPanel.cpp \
Widgets/WAnimParamsEditor.cpp \ Widgets/WAnimParamsEditor.cpp \
@ -185,7 +187,6 @@ SOURCES += \
CBasicViewport.cpp \ CBasicViewport.cpp \
CDarkStyle.cpp \ CDarkStyle.cpp \
CGizmo.cpp \ CGizmo.cpp \
CNodeSelection.cpp \
CSceneViewport.cpp \ CSceneViewport.cpp \
CStartWindow.cpp \ CStartWindow.cpp \
INodeEditor.cpp \ INodeEditor.cpp \
@ -193,8 +194,6 @@ SOURCES += \
TestDialog.cpp \ TestDialog.cpp \
UICommon.cpp \ UICommon.cpp \
CErrorLogDialog.cpp \ CErrorLogDialog.cpp \
Undo/CSelectAllCommand.cpp \
Undo/CInvertSelectionCommand.cpp \
WorldEditor/CPoiMapEditDialog.cpp \ WorldEditor/CPoiMapEditDialog.cpp \
WorldEditor/CPoiMapModel.cpp \ WorldEditor/CPoiMapModel.cpp \
PropertyEdit/CPropertyModel.cpp \ PropertyEdit/CPropertyModel.cpp \
@ -211,7 +210,9 @@ SOURCES += \
WorldEditor/CSelectInstanceDialog.cpp \ WorldEditor/CSelectInstanceDialog.cpp \
Undo/CAddLinkCommand.cpp \ Undo/CAddLinkCommand.cpp \
Undo/CDeleteLinksCommand.cpp \ Undo/CDeleteLinksCommand.cpp \
Undo/CEditLinkCommand.cpp Undo/CEditLinkCommand.cpp \
Undo/CDeleteSelectionCommand.cpp \
Undo/CCreateInstanceCommand.cpp
# UI Files # UI Files
FORMS += \ FORMS += \

View File

@ -1,11 +1,12 @@
#include "INodeEditor.h" #include "INodeEditor.h"
#include "CSelectionIterator.h"
#include "Editor/Undo/UndoCommands.h" #include "Editor/Undo/UndoCommands.h"
#include <QMouseEvent> #include <QMouseEvent>
INodeEditor::INodeEditor(QWidget *pParent) INodeEditor::INodeEditor(QWidget *pParent)
: QMainWindow(pParent) : QMainWindow(pParent)
, mPickMode(false) , mPickMode(false)
, mSelectionNodeFlags(eAllNodeTypes) , mpSelection(new CNodeSelection)
, mSelectionLocked(false) , mSelectionLocked(false)
, mShowGizmo(false) , mShowGizmo(false)
, mGizmoHovering(false) , mGizmoHovering(false)
@ -53,10 +54,12 @@ INodeEditor::INodeEditor(QWidget *pParent)
connect(mGizmoActions[2], SIGNAL(triggered()), this, SLOT(OnRotateTriggered())); connect(mGizmoActions[2], SIGNAL(triggered()), this, SLOT(OnRotateTriggered()));
connect(mGizmoActions[3], SIGNAL(triggered()), this, SLOT(OnScaleTriggered())); connect(mGizmoActions[3], SIGNAL(triggered()), this, SLOT(OnScaleTriggered()));
connect(mpTransformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnTransformSpaceChanged(int))); connect(mpTransformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnTransformSpaceChanged(int)));
connect(mpSelection, SIGNAL(Modified()), this, SLOT(OnSelectionModified()));
} }
INodeEditor::~INodeEditor() INodeEditor::~INodeEditor()
{ {
delete mpSelection;
} }
QUndoStack* INodeEditor::UndoStack() QUndoStack* INodeEditor::UndoStack()
@ -76,7 +79,7 @@ CGizmo* INodeEditor::Gizmo()
bool INodeEditor::IsGizmoVisible() bool INodeEditor::IsGizmoVisible()
{ {
return (mShowGizmo && !mSelection.empty()); return (mShowGizmo && !mpSelection->IsEmpty());
} }
void INodeEditor::BeginGizmoTransform() void INodeEditor::BeginGizmoTransform()
@ -113,33 +116,12 @@ ETransformSpace INodeEditor::CurrentTransformSpace()
} }
} }
void INodeEditor::RecalculateSelectionBounds()
{
mSelectionBounds = CAABox::skInfinite;
foreach (CSceneNode *pNode, mSelection)
ExpandSelectionBounds(pNode);
}
void INodeEditor::ExpandSelectionBounds(CSceneNode *pNode)
{
mSelectionBounds.ExpandBounds(pNode->AABox());
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
if (pScript->HasPreviewVolume())
mSelectionBounds.ExpandBounds(pScript->PreviewVolumeAABox());
}
}
void INodeEditor::SelectNode(CSceneNode *pNode) void INodeEditor::SelectNode(CSceneNode *pNode)
{ {
if (!mSelectionLocked) if (!mSelectionLocked)
{ {
if (!pNode->IsSelected()) if (!pNode->IsSelected())
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); mUndoStack.push(new CSelectNodeCommand(mpSelection, pNode));
} }
} }
@ -148,7 +130,7 @@ void INodeEditor::DeselectNode(CSceneNode *pNode)
if (!mSelectionLocked) if (!mSelectionLocked)
{ {
if (pNode->IsSelected()) if (pNode->IsSelected())
mUndoStack.push(new CDeselectNodeCommand(this, pNode, mSelection)); mUndoStack.push(new CDeselectNodeCommand(mpSelection, pNode));
} }
} }
@ -156,8 +138,8 @@ void INodeEditor::ClearSelection()
{ {
if (!mSelectionLocked) if (!mSelectionLocked)
{ {
if (!mSelection.empty()) if (!mpSelection->IsEmpty())
mUndoStack.push(new CClearSelectionCommand(this, mSelection)); mUndoStack.push(new CClearSelectionCommand(mpSelection));
} }
} }
@ -165,17 +147,17 @@ void INodeEditor::ClearAndSelectNode(CSceneNode *pNode)
{ {
if (!mSelectionLocked) if (!mSelectionLocked)
{ {
if (mSelection.empty()) if (mpSelection->IsEmpty())
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); mUndoStack.push(new CSelectNodeCommand(mpSelection, pNode));
else if ((mSelection.size() == 1) && (mSelection.front() == pNode)) else if ((mpSelection->Size() == 1) && (mpSelection->Front() == pNode))
return; return;
else else
{ {
mUndoStack.beginMacro("Select"); mUndoStack.beginMacro("Select");
mUndoStack.push(new CClearSelectionCommand(this, mSelection)); mUndoStack.push(new CClearSelectionCommand(mpSelection));
mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); mUndoStack.push(new CSelectNodeCommand(mpSelection, pNode));
mUndoStack.endMacro(); mUndoStack.endMacro();
} }
} }
@ -184,13 +166,13 @@ void INodeEditor::ClearAndSelectNode(CSceneNode *pNode)
void INodeEditor::SelectAll(FNodeFlags NodeFlags) void INodeEditor::SelectAll(FNodeFlags NodeFlags)
{ {
if (!mSelectionLocked) if (!mSelectionLocked)
mUndoStack.push(new CSelectAllCommand(this, mSelection, &mScene, NodeFlags)); mUndoStack.push(new CSelectAllCommand(mpSelection, &mScene, NodeFlags));
} }
void INodeEditor::InvertSelection(FNodeFlags NodeFlags) void INodeEditor::InvertSelection(FNodeFlags NodeFlags)
{ {
if (!mSelectionLocked) if (!mSelectionLocked)
mUndoStack.push(new CInvertSelectionCommand(this, mSelection, &mScene, NodeFlags)); mUndoStack.push(new CInvertSelectionCommand(mpSelection, &mScene, NodeFlags));
} }
void INodeEditor::SetSelectionLocked(bool Locked) void INodeEditor::SetSelectionLocked(bool Locked)
@ -200,12 +182,12 @@ void INodeEditor::SetSelectionLocked(bool Locked)
bool INodeEditor::HasSelection() const bool INodeEditor::HasSelection() const
{ {
return (!mSelection.isEmpty()); return (!mpSelection->IsEmpty());
} }
const QList<CSceneNode*>& INodeEditor::GetSelection() const CNodeSelection* INodeEditor::Selection() const
{ {
return mSelection; return mpSelection;
} }
void INodeEditor::EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor /*= Qt::CrossCursor*/) void INodeEditor::EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor /*= Qt::CrossCursor*/)
@ -231,22 +213,42 @@ void INodeEditor::ExitPickMode()
} }
} }
void INodeEditor::NotifySelectionModified() void INodeEditor::NotifySelectionTransformed()
{
for (CSelectionIterator It(mpSelection); It; ++It)
It->OnTransformed();
mpSelection->UpdateBounds();
emit SelectionTransformed();
}
void INodeEditor::NotifyNodeAboutToBeSpawned()
{
emit NodeAboutToBeSpawned();
}
void INodeEditor::NotifyNodeSpawned(CSceneNode *pNode)
{
emit NodeSpawned(pNode);
}
void INodeEditor::NotifyNodeAboutToBeDeleted(CSceneNode *pNode)
{
emit NodeAboutToBeDeleted(pNode);
}
void INodeEditor::NotifyNodeDeleted()
{
emit NodeDeleted();
}
// ************ PUBLIC SLOTS ************
void INodeEditor::OnSelectionModified()
{ {
RecalculateSelectionBounds();
UpdateSelectionUI(); UpdateSelectionUI();
emit SelectionModified(); emit SelectionModified();
} }
void INodeEditor::NotifySelectionTransformed()
{
foreach (CSceneNode *pNode, mSelection)
pNode->OnTransformed();
emit SelectionTransformed();
}
// ************ PUBLIC SLOTS ************
void INodeEditor::OnGizmoMoved() void INodeEditor::OnGizmoMoved()
{ {
switch (mGizmo.Mode()) switch (mGizmo.Mode())
@ -254,26 +256,25 @@ void INodeEditor::OnGizmoMoved()
case CGizmo::eTranslate: case CGizmo::eTranslate:
{ {
CVector3f delta = mGizmo.DeltaTranslation(); CVector3f delta = mGizmo.DeltaTranslation();
mUndoStack.push(new CTranslateNodeCommand(this, mSelection, delta, mTranslateSpace)); mUndoStack.push(new CTranslateNodeCommand(this, mpSelection->SelectedNodeList(), delta, mTranslateSpace));
break; break;
} }
case CGizmo::eRotate: case CGizmo::eRotate:
{ {
CQuaternion delta = mGizmo.DeltaRotation(); CQuaternion delta = mGizmo.DeltaRotation();
mUndoStack.push(new CRotateNodeCommand(this, mSelection, CVector3f::skZero, delta, mRotateSpace)); mUndoStack.push(new CRotateNodeCommand(this, mpSelection->SelectedNodeList(), CVector3f::skZero, delta, mRotateSpace));
break; break;
} }
case CGizmo::eScale: case CGizmo::eScale:
{ {
CVector3f delta = mGizmo.DeltaScale(); CVector3f delta = mGizmo.DeltaScale();
mUndoStack.push(new CScaleNodeCommand(this, mSelection, CVector3f::skZero, delta)); mUndoStack.push(new CScaleNodeCommand(this, mpSelection->SelectedNodeList(), CVector3f::skZero, delta));
break; break;
} }
} }
RecalculateSelectionBounds();
UpdateGizmoUI(); UpdateGizmoUI();
} }
@ -285,7 +286,7 @@ void INodeEditor::OnViewportClick(const SRayIntersection& rkRayIntersect, QMouse
// Not in pick mode: process node selection/deselection // Not in pick mode: process node selection/deselection
if (!mPickMode) if (!mPickMode)
{ {
bool ValidNode = (pNode && (pNode->NodeType() & mSelectionNodeFlags)); bool ValidNode = (pNode && mpSelection->IsAllowedType(pNode));
bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0); bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0);
bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0); bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0);
@ -364,11 +365,11 @@ void INodeEditor::UpdateTransformActionsEnabled()
bool AllowTranslate = true, AllowRotate = true, AllowScale = true; bool AllowTranslate = true, AllowRotate = true, AllowScale = true;
bool SelectedModeWasEnabled = mpGizmoGroup->checkedAction()->isEnabled(); bool SelectedModeWasEnabled = mpGizmoGroup->checkedAction()->isEnabled();
for (auto it = mSelection.begin(); it != mSelection.end(); it++) for (CSelectionIterator It(mpSelection); It; ++It)
{ {
if (!(*it)->AllowsTranslate()) AllowTranslate = false; if (!It->AllowsTranslate()) AllowTranslate = false;
if (!(*it)->AllowsRotate()) AllowRotate = false; if (!It->AllowsRotate()) AllowRotate = false;
if (!(*it)->AllowsScale()) AllowScale = false; if (!It->AllowsScale()) AllowScale = false;
} }
mGizmoActions[1]->setEnabled(AllowTranslate); mGizmoActions[1]->setEnabled(AllowTranslate);

View File

@ -2,6 +2,7 @@
#define INODEEDITOR_H #define INODEEDITOR_H
#include "CGizmo.h" #include "CGizmo.h"
#include "CNodeSelection.h"
#include <Math/ETransformSpace.h> #include <Math/ETransformSpace.h>
#include <Core/Scene/CScene.h> #include <Core/Scene/CScene.h>
@ -23,9 +24,7 @@ protected:
// Node management // Node management
CScene mScene; CScene mScene;
QList<CSceneNode*> mSelection; CNodeSelection *mpSelection;
FNodeFlags mSelectionNodeFlags;
CAABox mSelectionBounds;
bool mSelectionLocked; bool mSelectionLocked;
// Gizmo // Gizmo
@ -62,8 +61,6 @@ public:
void EndGizmoTransform(); void EndGizmoTransform();
ETransformSpace CurrentTransformSpace(); ETransformSpace CurrentTransformSpace();
void RecalculateSelectionBounds();
void ExpandSelectionBounds(CSceneNode *pNode);
void SelectNode(CSceneNode *pNode); void SelectNode(CSceneNode *pNode);
void DeselectNode(CSceneNode *pNode); void DeselectNode(CSceneNode *pNode);
void ClearSelection(); void ClearSelection();
@ -72,15 +69,22 @@ public:
void InvertSelection(FNodeFlags NodeFlags); void InvertSelection(FNodeFlags NodeFlags);
void SetSelectionLocked(bool Locked); void SetSelectionLocked(bool Locked);
bool HasSelection() const; bool HasSelection() const;
const QList<CSceneNode*>& GetSelection() const; CNodeSelection* Selection() const;
void EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor = Qt::CrossCursor); void EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor = Qt::CrossCursor);
void ExitPickMode(); void ExitPickMode();
void NotifySelectionModified();
void NotifySelectionTransformed(); void NotifySelectionTransformed();
virtual void NotifyNodeAboutToBeSpawned();
virtual void NotifyNodeSpawned(CSceneNode *pNode);
virtual void NotifyNodeAboutToBeDeleted(CSceneNode *pNode);
virtual void NotifyNodeDeleted();
signals: signals:
void NodeAboutToBeSpawned();
void NodeSpawned(CSceneNode *pNode);
void NodeAboutToBeDeleted(CSceneNode *pNode);
void NodeDeleted();
void SelectionModified(); void SelectionModified();
void SelectionTransformed(); void SelectionTransformed();
@ -90,6 +94,7 @@ signals:
void PickModeHoverChanged(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent); void PickModeHoverChanged(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent);
public slots: public slots:
void OnSelectionModified();
void OnGizmoMoved(); void OnGizmoMoved();
virtual void UpdateGizmoUI() = 0; virtual void UpdateGizmoUI() = 0;
virtual void UpdateSelectionUI() = 0; virtual void UpdateSelectionUI() = 0;

View File

@ -499,7 +499,7 @@ Qt::ItemFlags CPropertyModel::flags(const QModelIndex& rkIndex) const
else return (Qt::ItemIsEnabled | Qt::ItemIsEditable); else return (Qt::ItemIsEnabled | Qt::ItemIsEditable);
} }
void CPropertyModel::NotifyPropertyModified(IProperty *pProp) void CPropertyModel::NotifyPropertyModified(class CScriptObject*, IProperty *pProp)
{ {
NotifyPropertyModified(IndexForProperty(pProp)); NotifyPropertyModified(IndexForProperty(pProp));
} }

View File

@ -35,7 +35,7 @@ public:
inline void SetBoldModifiedProperties(bool Enable) { mBoldModifiedProperties = Enable; } inline void SetBoldModifiedProperties(bool Enable) { mBoldModifiedProperties = Enable; }
public slots: public slots:
void NotifyPropertyModified(IProperty *pProp); void NotifyPropertyModified(class CScriptObject *pInst, IProperty *pProp);
void NotifyPropertyModified(const QModelIndex& rkIndex); void NotifyPropertyModified(const QModelIndex& rkIndex);
signals: signals:

View File

@ -75,7 +75,7 @@ void CPropertyView::SetEditor(CWorldEditor *pEditor)
{ {
mpEditor = pEditor; mpEditor = pEditor;
mpDelegate->SetEditor(pEditor); mpDelegate->SetEditor(pEditor);
connect(mpEditor, SIGNAL(PropertyModified(IProperty*,bool)), mpModel, SLOT(NotifyPropertyModified(IProperty*))); connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), mpModel, SLOT(NotifyPropertyModified(CScriptObject*,IProperty*)));
} }
void CPropertyView::SetInstance(CScriptObject *pObj) void CPropertyView::SetInstance(CScriptObject *pObj)

View File

@ -1,42 +0,0 @@
#include "CClearSelectionCommand.h"
#include "Editor/INodeEditor.h"
CClearSelectionCommand::CClearSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& selection)
: IUndoCommand("Clear Selection"),
mpEditor(pEditor),
mSelectionState(selection),
mpSelection(&selection)
{
}
CClearSelectionCommand::~CClearSelectionCommand()
{
}
void CClearSelectionCommand::undo()
{
mpSelection->reserve(mSelectionState.size());
foreach (CSceneNode *pNode, mSelectionState)
{
if (!pNode->IsSelected())
{
pNode->SetSelected(true);
mpSelection->push_back(pNode);
}
}
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionModified();
}
void CClearSelectionCommand::redo()
{
foreach (CSceneNode *pNode, *mpSelection)
if (pNode->IsSelected())
pNode->SetSelected(false);
mpSelection->clear();
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionModified();
}

View File

@ -7,14 +7,18 @@
class CClearSelectionCommand : public IUndoCommand class CClearSelectionCommand : public IUndoCommand
{ {
INodeEditor *mpEditor; QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mSelectionState; CNodeSelection *mpSelection;
QList<CSceneNode*> *mpSelection;
public: public:
CClearSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& selection); CClearSelectionCommand(CNodeSelection *pSelection)
~CClearSelectionCommand(); : IUndoCommand("Clear Selection"),
void undo(); mOldSelection(pSelection->SelectedNodeList()),
void redo(); mpSelection(pSelection)
{}
void undo() { mpSelection->SetSelectedNodes(mOldSelection); }
void redo() { mpSelection->Clear(); }
bool AffectsCleanState() const { return false; } bool AffectsCleanState() const { return false; }
}; };

View File

@ -0,0 +1,39 @@
#include "CCreateInstanceCommand.h"
CCreateInstanceCommand::CCreateInstanceCommand(CWorldEditor *pEditor, CScriptTemplate *pTemplate, CScriptLayer *pLayer, const CVector3f& rkPosition)
: IUndoCommand("Create Instance")
, mpEditor(pEditor)
, mpScene(pEditor->Scene())
, mpArea(pEditor->ActiveArea())
, mpTemplate(pTemplate)
, mLayerIndex(pLayer->AreaIndex())
, mSpawnPosition(rkPosition)
, mOldSelection(pEditor->Selection()->SelectedNodeList())
, mpNewInstance(nullptr)
, mpNewNode(nullptr)
{
}
void CCreateInstanceCommand::undo()
{
mpEditor->NotifyNodeAboutToBeDeleted(mpNewNode);
mpEditor->Selection()->SetSelectedNodes(mOldSelection);
mpScene->DeleteNode(mpNewNode);
mpArea->DeleteInstance(mpNewInstance);
mpNewNode = nullptr;
mpNewInstance = nullptr;
mpEditor->NotifyNodeDeleted();
}
void CCreateInstanceCommand::redo()
{
mpEditor->NotifyNodeAboutToBeSpawned();
CScriptLayer *pLayer = (mLayerIndex == -1 ? mpArea->GetGeneratorLayer() : mpArea->GetScriptLayer(mLayerIndex));
mpNewInstance = mpArea->SpawnInstance(mpTemplate, pLayer, mSpawnPosition);
mpNewNode = mpScene->CreateScriptNode(mpNewInstance);
mpNewNode->SetPosition(mSpawnPosition);
mpEditor->NotifyNodeSpawned(mpNewNode);
mpEditor->Selection()->ClearAndSelectNode(mpNewNode);
}

View File

@ -0,0 +1,32 @@
#ifndef CCREATEINSTANCECOMMAND_H
#define CCREATEINSTANCECOMMAND_H
#include "IUndoCommand.h"
#include "CClearSelectionCommand.h"
#include "CSelectNodeCommand.h"
#include "Editor/WorldEditor/CWorldEditor.h"
#include <Core/Resource/CGameArea.h>
#include <Core/Resource/Script/CScriptLayer.h>
#include <Core/Resource/Script/CScriptObject.h>
class CCreateInstanceCommand : public IUndoCommand
{
CWorldEditor *mpEditor;
CScene *mpScene;
CGameArea *mpArea;
CScriptTemplate *mpTemplate;
u32 mLayerIndex;
CVector3f mSpawnPosition;
QList<CSceneNode*> mOldSelection;
CScriptObject *mpNewInstance;
CScriptNode *mpNewNode;
public:
CCreateInstanceCommand(CWorldEditor *pEditor, CScriptTemplate *pTemplate, CScriptLayer *pLayer, const CVector3f& rkPosition);
void undo();
void redo();
bool AffectsCleanState() const { return true; }
};
#endif // CCREATEINSTANCECOMMAND_H

View File

@ -0,0 +1,38 @@
#include "CDeleteSelectionCommand.h"
#include "Editor/CSelectionIterator.h"
CDeleteSelectionCommand::CDeleteSelectionCommand(CWorldEditor *pEditor)
: IUndoCommand("Delete")
, mpEditor(pEditor)
{
mOldSelection = pEditor->Selection()->SelectedNodeList();
for (CSelectionIterator It(pEditor->Selection()); It; ++It)
{
if (It->NodeType() == eScriptNode)
mNodesToDelete << *It;
else
mNewSelection << *It;
}
}
void CDeleteSelectionCommand::undo()
{
//foreach (CSceneNode *pNode, mNodesToDelete)
// pNode->SetDeleted(false);
//mpEditor->Selection()->SetSelectedNodes(mOldSelection);
}
void CDeleteSelectionCommand::redo()
{
mpEditor->Selection()->SetSelectedNodes(mNewSelection);
foreach (CSceneNode *pNode, mNodesToDelete)
{
CScriptObject *pInst = static_cast<CScriptNode*>(pNode)->Object();
mpEditor->NotifyNodeAboutToBeDeleted(pNode);
mpEditor->Scene()->DeleteNode(pNode);
mpEditor->ActiveArea()->DeleteInstance(pInst);
mpEditor->NotifyNodeDeleted();
}
}

View File

@ -0,0 +1,22 @@
#ifndef CDELETESELECTIONCOMMAND_H
#define CDELETESELECTIONCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/WorldEditor/CWorldEditor.h"
#include <Core/Scene/CSceneNode.h>
class CDeleteSelectionCommand : public IUndoCommand
{
CWorldEditor *mpEditor;
QList<CSceneNode*> mNodesToDelete;
QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mNewSelection;
public:
CDeleteSelectionCommand(CWorldEditor *pEditor);
void undo();
void redo();
bool AffectsCleanState() const { return true; }
};
#endif // CDELETESELECTIONCOMMAND_H

View File

@ -1,42 +0,0 @@
#include "CDeselectNodeCommand.h"
#include "Editor/INodeEditor.h"
CDeselectNodeCommand::CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection)
: IUndoCommand("Deselect"),
mpEditor(pEditor),
mpNode(pNode),
mpSelection(&selection)
{
}
void CDeselectNodeCommand::undo()
{
if (!mpNode->IsSelected())
{
mpNode->SetSelected(true);
mpSelection->push_back(mpNode);
}
mpEditor->ExpandSelectionBounds(mpNode);
mpEditor->NotifySelectionModified();
}
void CDeselectNodeCommand::redo()
{
if (mpNode->IsSelected())
{
mpNode->SetSelected(false);
for (auto it = mpSelection->begin(); it != mpSelection->end(); it++)
{
if (*it == mpNode)
{
mpSelection->erase(it);
break;
}
}
}
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionModified();
}

View File

@ -7,13 +7,17 @@
class CDeselectNodeCommand : public IUndoCommand class CDeselectNodeCommand : public IUndoCommand
{ {
INodeEditor *mpEditor;
CSceneNode *mpNode; CSceneNode *mpNode;
QList<CSceneNode*> *mpSelection; CNodeSelection *mpSelection;
public: public:
CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection); CDeselectNodeCommand(CNodeSelection *pSelection, CSceneNode *pNode)
void undo(); : IUndoCommand("Deselect")
void redo(); , mpNode(pNode)
, mpSelection(pSelection)
{}
void undo() { mpSelection->SelectNode(mpNode); }
void redo() { mpSelection->DeselectNode(mpNode); }
bool AffectsCleanState() const { return false; } bool AffectsCleanState() const { return false; }
}; };

View File

@ -1,56 +0,0 @@
#include "CInvertSelectionCommand.h"
#include <Core/Scene/CSceneIterator.h>
CInvertSelectionCommand::CInvertSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags)
: IUndoCommand("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->NotifySelectionModified();
}
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->NotifySelectionModified();
}

View File

@ -7,17 +7,27 @@
class CInvertSelectionCommand : public IUndoCommand class CInvertSelectionCommand : public IUndoCommand
{ {
INodeEditor *mpEditor; CNodeSelection *mpSelection;
QList<CSceneNode*> mOldSelection; QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mNewSelection; QList<CSceneNode*> mNewSelection;
QList<CSceneNode*> *mpSelection;
public: public:
CInvertSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags); CInvertSelectionCommand(CNodeSelection *pSelection, CScene *pScene, FNodeFlags NodeFlags)
~CInvertSelectionCommand(); : IUndoCommand("Invert Selection")
void undo(); , mpSelection(pSelection)
void redo(); {
virtual bool AffectsCleanState() const { return false; } for (CSceneIterator It(pScene, NodeFlags); It; ++It)
{
if (It->IsSelected())
mOldSelection << *It;
else
mNewSelection << *It;
}
}
void undo() { mpSelection->SetSelectedNodes(mOldSelection); }
void redo() { mpSelection->SetSelectedNodes(mNewSelection); }
bool AffectsCleanState() const { return false; }
}; };
#endif // CINVERTSELECTIONCOMMAND_H #endif // CINVERTSELECTIONCOMMAND_H

View File

@ -79,7 +79,6 @@ void CRotateNodeCommand::undo()
rotate.pNode->SetRotation(rotate.initialRot); rotate.pNode->SetRotation(rotate.initialRot);
} }
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionTransformed(); mpEditor->NotifySelectionTransformed();
mpEditor->UpdateGizmoUI(); mpEditor->UpdateGizmoUI();
} }
@ -94,7 +93,6 @@ void CRotateNodeCommand::redo()
rotate.pNode->SetRotation(rotate.newRot); rotate.pNode->SetRotation(rotate.newRot);
} }
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionTransformed(); mpEditor->NotifySelectionTransformed();
mpEditor->UpdateGizmoUI(); mpEditor->UpdateGizmoUI();
} }

View File

@ -79,7 +79,6 @@ void CScaleNodeCommand::undo()
scale.pNode->SetScale(scale.initialScale); scale.pNode->SetScale(scale.initialScale);
} }
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionTransformed(); mpEditor->NotifySelectionTransformed();
mpEditor->UpdateGizmoUI(); mpEditor->UpdateGizmoUI();
} }
@ -94,7 +93,6 @@ void CScaleNodeCommand::redo()
scale.pNode->SetScale(scale.newScale); scale.pNode->SetScale(scale.newScale);
} }
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionTransformed(); mpEditor->NotifySelectionTransformed();
mpEditor->UpdateGizmoUI(); mpEditor->UpdateGizmoUI();
} }

View File

@ -1,56 +0,0 @@
#include "CSelectAllCommand.h"
#include <Core/Scene/CSceneIterator.h>
CSelectAllCommand::CSelectAllCommand(INodeEditor *pEditor, QList<CSceneNode *> &rSelection, CScene *pScene, FNodeFlags NodeFlags)
: IUndoCommand("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->NotifySelectionModified();
}
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->NotifySelectionModified();
}

View File

@ -7,16 +7,22 @@
class CSelectAllCommand : public IUndoCommand class CSelectAllCommand : public IUndoCommand
{ {
INodeEditor *mpEditor;
QList<CSceneNode*> mOldSelection; QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mNewSelection; QList<CSceneNode*> mNewSelection;
QList<CSceneNode*> *mpSelection; CNodeSelection *mpSelection;
public: public:
CSelectAllCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags); CSelectAllCommand(CNodeSelection *pSelection, CScene *pScene, FNodeFlags NodeFlags)
~CSelectAllCommand(); : IUndoCommand("Select All")
void undo(); , mOldSelection(pSelection->SelectedNodeList())
void redo(); , mpSelection(pSelection)
{
for (CSceneIterator It(pScene, NodeFlags); It; ++It)
mNewSelection << *It;
}
void undo() { mpSelection->SetSelectedNodes(mOldSelection); }
void redo() { mpSelection->SetSelectedNodes(mNewSelection); }
bool AffectsCleanState() const { return false; } bool AffectsCleanState() const { return false; }
}; };

View File

@ -1,42 +0,0 @@
#include "CSelectNodeCommand.h"
#include "Editor/INodeEditor.h"
CSelectNodeCommand::CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection)
: IUndoCommand("Select"),
mpEditor(pEditor),
mpNode(pNode),
mpSelection(&selection)
{
}
void CSelectNodeCommand::undo()
{
if (mpNode->IsSelected())
{
mpNode->SetSelected(false);
for (auto it = mpSelection->begin(); it != mpSelection->end(); it++)
{
if (*it == mpNode)
{
mpSelection->erase(it);
break;
}
}
}
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionModified();
}
void CSelectNodeCommand::redo()
{
if (!mpNode->IsSelected())
{
mpNode->SetSelected(true);
mpSelection->push_back(mpNode);
}
mpEditor->ExpandSelectionBounds(mpNode);
mpEditor->NotifySelectionModified();
}

View File

@ -7,13 +7,18 @@
class CSelectNodeCommand : public IUndoCommand class CSelectNodeCommand : public IUndoCommand
{ {
INodeEditor *mpEditor;
CSceneNode *mpNode; CSceneNode *mpNode;
QList<CSceneNode*> *mpSelection; CNodeSelection *mpSelection;
public: public:
CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection); CSelectNodeCommand(CNodeSelection *pSelection, CSceneNode *pNode)
void undo(); : IUndoCommand("Select")
void redo(); , mpNode(pNode)
, mpSelection(pSelection)
{}
void undo() { mpSelection->DeselectNode(mpNode); }
void redo() { mpSelection->SelectNode(mpNode); }
bool AffectsCleanState() const { return false; } bool AffectsCleanState() const { return false; }
}; };

View File

@ -71,7 +71,6 @@ void CTranslateNodeCommand::undo()
foreach (SNodeTranslate translate, mNodeList) foreach (SNodeTranslate translate, mNodeList)
translate.pNode->SetPosition(translate.initialPos); translate.pNode->SetPosition(translate.initialPos);
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionTransformed(); mpEditor->NotifySelectionTransformed();
mpEditor->UpdateGizmoUI(); mpEditor->UpdateGizmoUI();
} }
@ -83,7 +82,6 @@ void CTranslateNodeCommand::redo()
foreach (SNodeTranslate translate, mNodeList) foreach (SNodeTranslate translate, mNodeList)
translate.pNode->SetPosition(translate.newPos); translate.pNode->SetPosition(translate.newPos);
mpEditor->RecalculateSelectionBounds();
mpEditor->NotifySelectionTransformed(); mpEditor->NotifySelectionTransformed();
mpEditor->UpdateGizmoUI(); mpEditor->UpdateGizmoUI();
} }

View File

@ -1,6 +1,8 @@
#ifndef UNDOCOMMANDS #ifndef UNDOCOMMANDS
#define UNDOCOMMANDS #define UNDOCOMMANDS
#include "CCreateInstanceCommand.h"
#include "CTranslateNodeCommand.h" #include "CTranslateNodeCommand.h"
#include "CRotateNodeCommand.h" #include "CRotateNodeCommand.h"
#include "CScaleNodeCommand.h" #include "CScaleNodeCommand.h"
@ -10,6 +12,7 @@
#include "CClearSelectionCommand.h" #include "CClearSelectionCommand.h"
#include "CSelectAllCommand.h" #include "CSelectAllCommand.h"
#include "CInvertSelectionCommand.h" #include "CInvertSelectionCommand.h"
#include "CDeleteSelectionCommand.h"
#include "CEditScriptPropertyCommand.h" #include "CEditScriptPropertyCommand.h"
#include "CResizeScriptArrayCommand.h" #include "CResizeScriptArrayCommand.h"

View File

@ -26,11 +26,6 @@
#define TYPES_NODE_TYPE_SHIFT 1 #define TYPES_NODE_TYPE_SHIFT 1
#define TYPES_ITEM_TYPE_SHIFT 0 #define TYPES_ITEM_TYPE_SHIFT 0
bool SortTemplatesAlphabetical(CScriptTemplate *pA, CScriptTemplate *pB)
{
return (pA->Name() < pB->Name());
}
CInstancesModel::CInstancesModel(QObject *pParent) : QAbstractItemModel(pParent) CInstancesModel::CInstancesModel(QObject *pParent) : QAbstractItemModel(pParent)
{ {
mpEditor = nullptr; mpEditor = nullptr;
@ -40,6 +35,7 @@ CInstancesModel::CInstancesModel(QObject *pParent) : QAbstractItemModel(pParent)
mModelType = eLayers; mModelType = eLayers;
mShowColumnEnabled = true; mShowColumnEnabled = true;
mBaseItems << "Script"; mBaseItems << "Script";
mAddingOrRemovingRows = false;
} }
CInstancesModel::~CInstancesModel() CInstancesModel::~CInstancesModel()
@ -319,6 +315,11 @@ void CInstancesModel::SetEditor(CWorldEditor *pEditor)
mpEditor = pEditor; mpEditor = pEditor;
mpScene = (pEditor ? pEditor->Scene() : nullptr); mpScene = (pEditor ? pEditor->Scene() : nullptr);
connect(mpEditor, SIGNAL(NodeAboutToBeSpawned()), this, SLOT(NodeAboutToBeCreated()));
connect(mpEditor, SIGNAL(NodeSpawned(CSceneNode*)), this, SLOT(NodeCreated(CSceneNode*)));
connect(mpEditor, SIGNAL(NodeAboutToBeDeleted(CSceneNode*)), this, SLOT(NodeAboutToBeDeleted(CSceneNode*)));
connect(mpEditor, SIGNAL(NodeDeleted()), this, SLOT(NodeDeleted()));
connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), this, SLOT(PropertyModified(CScriptObject*,IProperty*)));
connect(mpEditor, SIGNAL(InstancesLayerAboutToChange()), this, SLOT(InstancesLayerPreChange())); connect(mpEditor, SIGNAL(InstancesLayerAboutToChange()), this, SLOT(InstancesLayerPreChange()));
connect(mpEditor, SIGNAL(InstancesLayerChanged(QList<CScriptNode*>)), this, SLOT(InstancesLayerPostChange(QList<CScriptNode*>))); connect(mpEditor, SIGNAL(InstancesLayerChanged(QList<CScriptNode*>)), this, SLOT(InstancesLayerPostChange(QList<CScriptNode*>)));
} }
@ -347,52 +348,6 @@ void CInstancesModel::SetShowColumnEnabled(bool Enabled)
emit layoutChanged(); emit layoutChanged();
} }
void CInstancesModel::NodeCreated(CSceneNode *pNode)
{
if (mModelType == eTypes)
{
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
pObj->Template()->SortObjects();
if (pObj->Template()->NumObjects() == 1)
{
mTemplateList << pObj->Template();
qSort(mTemplateList.begin(), mTemplateList.end(), SortTemplatesAlphabetical);
emit layoutChanged();
}
}
}
}
void CInstancesModel::NodeDeleted(CSceneNode *pNode)
{
if (mModelType = eTypes)
{
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
if (pObj->Template()->NumObjects() == 0)
{
for (auto it = mTemplateList.begin(); it != mTemplateList.end(); it++)
{
if (*it == pObj->Template())
{
mTemplateList.erase(it);
break;
}
}
emit layoutChanged();
}
}
}
}
CScriptLayer* CInstancesModel::IndexLayer(const QModelIndex& index) const CScriptLayer* CInstancesModel::IndexLayer(const QModelIndex& index) const
{ {
if ((mModelType != eLayers) || (IndexNodeType(index) != eScriptType) || (IndexType(index) != eObjectTypeIndex)) if ((mModelType != eLayers) || (IndexNodeType(index) != eScriptType) || (IndexType(index) != eObjectTypeIndex))
@ -420,6 +375,107 @@ CScriptObject* CInstancesModel::IndexObject(const QModelIndex& index) const
} }
// ************ PUBLIC SLOTS ************ // ************ PUBLIC SLOTS ************
void CInstancesModel::NodeAboutToBeCreated()
{
if (!mAddingOrRemovingRows)
{
emit layoutAboutToBeChanged();
mAddingOrRemovingRows = true;
}
}
void CInstancesModel::NodeCreated(CSceneNode *pNode)
{
if (mModelType == eTypes)
{
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
pObj->Template()->SortObjects();
if (pObj->Template()->NumObjects() == 1)
{
QModelIndex ScriptRootIdx = index(0, 0, QModelIndex());
int NewIndex = 0;
for (; NewIndex < mTemplateList.size(); NewIndex++)
{
if (mTemplateList[NewIndex]->Name() > pObj->Template()->Name())
break;
}
beginInsertRows(ScriptRootIdx, NewIndex, NewIndex);
mTemplateList.insert(NewIndex, pObj->Template());
endInsertRows();
}
}
}
emit layoutChanged();
mAddingOrRemovingRows = false;
}
void CInstancesModel::NodeAboutToBeDeleted(CSceneNode *pNode)
{
if (!mAddingOrRemovingRows)
{
emit layoutAboutToBeChanged();
mAddingOrRemovingRows = true;
}
if (mModelType == eTypes)
{
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
if (pObj->Template()->NumObjects() <= 1)
{
QModelIndex ScriptRootIdx = index(0, 0, QModelIndex());
int TempIdx = mTemplateList.indexOf(pObj->Template());
beginRemoveRows(ScriptRootIdx, TempIdx, TempIdx);
mTemplateList.removeOne(pObj->Template());
endRemoveRows();
}
}
}
}
void CInstancesModel::NodeDeleted()
{
emit layoutChanged();
mAddingOrRemovingRows = false;
}
void CInstancesModel::PropertyModified(CScriptObject *pInst, IProperty *pProp)
{
if (pInst->InstanceNameProperty() == pProp)
{
QModelIndex ScriptRoot = index(0, 0, QModelIndex());
if (mModelType == eLayers)
{
u32 Index = pInst->Layer()->AreaIndex();
QModelIndex LayerIndex = index(Index, 0, ScriptRoot);
QModelIndex InstIndex = index(pInst->LayerIndex(), 0, LayerIndex);
emit dataChanged(InstIndex, InstIndex);
}
else
{
u32 Index = mTemplateList.indexOf(pInst->Template());
QModelIndex TempIndex = index(Index, 0, ScriptRoot);
QList<CScriptObject*> InstList = QList<CScriptObject*>::fromStdList(pInst->Template()->ObjectList());
u32 InstIdx = InstList.indexOf(pInst);
QModelIndex InstIndex = index(InstIdx, 0, TempIndex);
emit dataChanged(InstIndex, InstIndex);
}
}
}
void CInstancesModel::InstancesLayerPreChange() void CInstancesModel::InstancesLayerPreChange()
{ {
// This is only really needed on layers, which have rows moved. // This is only really needed on layers, which have rows moved.
@ -501,7 +557,9 @@ void CInstancesModel::GenerateList()
mTemplateList << pTemp; mTemplateList << pTemp;
} }
qSort(mTemplateList.begin(), mTemplateList.end(), SortTemplatesAlphabetical); qSort(mTemplateList.begin(), mTemplateList.end(), [](CScriptTemplate *pLeft, CScriptTemplate *pRight) -> bool {
return (pLeft->Name() < pRight->Name());
});
} }
endResetModel(); endResetModel();

View File

@ -37,6 +37,7 @@ private:
QList<CScriptTemplate*> mTemplateList; QList<CScriptTemplate*> mTemplateList;
QStringList mBaseItems; QStringList mBaseItems;
bool mShowColumnEnabled; bool mShowColumnEnabled;
bool mAddingOrRemovingRows;
public: public:
explicit CInstancesModel(QObject *pParent = 0); explicit CInstancesModel(QObject *pParent = 0);
@ -53,13 +54,17 @@ public:
void SetArea(CGameArea *pArea); void SetArea(CGameArea *pArea);
void SetModelType(EInstanceModelType type); void SetModelType(EInstanceModelType type);
void SetShowColumnEnabled(bool Enabled); void SetShowColumnEnabled(bool Enabled);
void NodeCreated(CSceneNode *pNode);
void NodeDeleted(CSceneNode *pNode);
CScriptLayer* IndexLayer(const QModelIndex& index) const; CScriptLayer* IndexLayer(const QModelIndex& index) const;
CScriptTemplate* IndexTemplate(const QModelIndex& index) const; CScriptTemplate* IndexTemplate(const QModelIndex& index) const;
CScriptObject* IndexObject(const QModelIndex& index) const; CScriptObject* IndexObject(const QModelIndex& index) const;
public slots: public slots:
void NodeAboutToBeCreated();
void NodeCreated(CSceneNode *pNode);
void NodeAboutToBeDeleted(CSceneNode *pNode);
void NodeDeleted();
void PropertyModified(CScriptObject *pInst, IProperty *pProp);
void InstancesLayerPreChange(); void InstancesLayerPreChange();
void InstancesLayerPostChange(const QList<CScriptNode*>& rkInstanceList); void InstancesLayerPostChange(const QList<CScriptNode*>& rkInstanceList);

View File

@ -48,7 +48,7 @@ QVariant CLinkModel::data(const QModelIndex &index, int role) const
case 0: // Column 0 - Target Object case 0: // Column 0 - Target Object
{ {
u32 TargetID = (mType == eIncoming ? pLink->SenderID() : pLink->ReceiverID()); u32 TargetID = (mType == eIncoming ? pLink->SenderID() : pLink->ReceiverID());
CScriptObject *pTarget = mpObject->Area()->GetInstanceByID(TargetID); CScriptObject *pTarget = mpObject->Area()->InstanceByID(TargetID);
if (pTarget) { if (pTarget) {
QString ObjType = QString("[%1] ").arg(UICommon::ToQString(pTarget->Template()->Name())); QString ObjType = QString("[%1] ").arg(UICommon::ToQString(pTarget->Template()->Name()));

View File

@ -69,7 +69,7 @@ QVariant CPoiMapModel::data(const QModelIndex& rkIndex, int Role) const
if (rkIndex.row() < rowCount(QModelIndex())) if (rkIndex.row() < rowCount(QModelIndex()))
{ {
const CPoiToWorld::SPoiMap *pkMap = mpPoiToWorld->MapByIndex(rkIndex.row()); const CPoiToWorld::SPoiMap *pkMap = mpPoiToWorld->MapByIndex(rkIndex.row());
CScriptObject *pPOI = mpArea->GetInstanceByID(pkMap->PoiID); CScriptObject *pPOI = mpArea->InstanceByID(pkMap->PoiID);
if (Role == Qt::DisplayRole) if (Role == Qt::DisplayRole)
{ {

View File

@ -0,0 +1,124 @@
#ifndef CTEMPLATELISTVIEW
#define CTEMPLATELISTVIEW
#include "CTemplateMimeData.h"
#include "Editor/UICommon.h"
#include <Core/Resource/Script/CMasterTemplate.h>
#include <QAbstractListModel>
#include <QDrag>
#include <QListView>
class CTemplateListModel : public QAbstractListModel
{
Q_OBJECT
CMasterTemplate *mpMaster;
QList<CScriptTemplate*> mTemplates;
public:
CTemplateListModel(QObject *pParent = 0)
: QAbstractListModel(pParent)
{}
int rowCount(const QModelIndex&) const
{
return mTemplates.size();
}
QVariant data(const QModelIndex& rkIndex, int Role) const
{
if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole)
return TO_QSTRING(mTemplates[rkIndex.row()]->Name());
else
return QVariant::Invalid;
}
Qt::DropActions supportedDropActions() const
{
return Qt::IgnoreAction;
}
Qt::DropActions supportedDragActions() const
{
return Qt::MoveAction;
}
QMimeData* mimeData(const QModelIndexList& rkIndices) const
{
if (rkIndices.size() != 1) return nullptr;
QModelIndex Index = rkIndices.front();
CScriptTemplate *pTemp = TemplateForIndex(Index);
return new CTemplateMimeData(pTemp);
}
Qt::ItemFlags flags(const QModelIndex&) const
{
return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled;
}
void SetMaster(CMasterTemplate *pMaster)
{
beginResetModel();
mpMaster = pMaster;
mTemplates.clear();
for (u32 iTemp = 0; iTemp < mpMaster->NumScriptTemplates(); iTemp++)
mTemplates << mpMaster->TemplateByIndex(iTemp);
qSort(mTemplates.begin(), mTemplates.end(), [](CScriptTemplate *pLeft, CScriptTemplate *pRight) -> bool {
return pLeft->Name() < pRight->Name();
});
endResetModel();
}
inline CScriptTemplate* TemplateForIndex(const QModelIndex& rkIndex) const
{
return mTemplates[rkIndex.row()];
}
};
class CTemplateListView : public QListView
{
Q_OBJECT
CTemplateListModel *mpModel;
public:
CTemplateListView(QWidget *pParent = 0)
: QListView(pParent)
{
setModel(new CTemplateListModel(this));
}
void setModel(QAbstractItemModel *pModel)
{
if (CTemplateListModel *pTempModel = qobject_cast<CTemplateListModel*>(pModel))
mpModel = pTempModel;
else
mpModel = nullptr;
QListView::setModel(mpModel);
}
inline void SetMaster(CMasterTemplate *pMaster)
{
if (mpModel) mpModel->SetMaster(pMaster);
}
protected:
void startDrag(Qt::DropActions)
{
QModelIndexList Selection = selectionModel()->selectedRows();
if (Selection.size() == 1 && mpModel)
{
QDrag *pDrag = new QDrag(this);
pDrag->setMimeData(mpModel->mimeData(Selection));
pDrag->setPixmap(QPixmap());
pDrag->exec();
}
}
};
#endif // CTEMPLATELISTVIEW

View File

@ -0,0 +1,20 @@
#ifndef CTEMPLATEMIMEDATA_H
#define CTEMPLATEMIMEDATA_H
#include <Core/Resource/Script/CScriptTemplate.h>
#include <QMimeData>
class CTemplateMimeData : public QMimeData
{
Q_OBJECT
CScriptTemplate *mpTemplate;
public:
CTemplateMimeData(CScriptTemplate *pTemplate)
: mpTemplate(pTemplate)
{}
inline CScriptTemplate* Template() const { return mpTemplate; }
};
#endif // CTEMPLATEMIMEDATA_H

View File

@ -1,17 +1,18 @@
#include "CWorldEditor.h" #include "CWorldEditor.h"
#include "ui_CWorldEditor.h" #include "ui_CWorldEditor.h"
#include "CConfirmUnlinkDialog.h" #include "CConfirmUnlinkDialog.h"
#include "CLayerEditor.h" #include "CLayerEditor.h"
#include "CTemplateMimeData.h"
#include "WModifyTab.h" #include "WModifyTab.h"
#include "WInstancesTab.h" #include "WInstancesTab.h"
#include "Editor/CBasicViewport.h" #include "Editor/CBasicViewport.h"
#include "Editor/CSelectionIterator.h"
#include "Editor/UICommon.h"
#include "Editor/PropertyEdit/CPropertyView.h" #include "Editor/PropertyEdit/CPropertyView.h"
#include "Editor/Widgets/WDraggableSpinBox.h" #include "Editor/Widgets/WDraggableSpinBox.h"
#include "Editor/Widgets/WVectorEditor.h" #include "Editor/Widgets/WVectorEditor.h"
#include "Editor/Undo/UndoCommands.h" #include "Editor/Undo/UndoCommands.h"
#include "Editor/UICommon.h"
#include <Core/Render/CDrawUtil.h> #include <Core/Render/CDrawUtil.h>
#include <Core/Resource/Cooker/CAreaCooker.h> #include <Core/Resource/Cooker/CAreaCooker.h>
@ -38,7 +39,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
Log::Write("Creating World Editor"); Log::Write("Creating World Editor");
ui->setupUi(this); ui->setupUi(this);
mSelectionNodeFlags = eScriptNode | eLightNode; mpSelection->SetAllowedNodeTypes(eScriptNode | eLightNode);
// Start refresh timer // Start refresh timer
connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport())); connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport()));
@ -51,6 +52,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
// Initialize UI stuff // Initialize UI stuff
ui->MainViewport->SetScene(this, &mScene); ui->MainViewport->SetScene(this, &mScene);
ui->CreateTabContents->SetEditor(this);
ui->ModifyTabContents->SetEditor(this); ui->ModifyTabContents->SetEditor(this);
ui->InstancesTabContents->SetEditor(this, &mScene); ui->InstancesTabContents->SetEditor(this, &mScene);
ui->TransformSpinBox->SetOrientation(Qt::Horizontal); ui->TransformSpinBox->SetOrientation(Qt::Horizontal);
@ -66,6 +68,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
// Initialize actions // Initialize actions
addAction(ui->ActionIncrementGizmo); addAction(ui->ActionIncrementGizmo);
addAction(ui->ActionDecrementGizmo); addAction(ui->ActionDecrementGizmo);
addAction(ui->ActionDelete);
QAction *pToolBarUndo = mUndoStack.createUndoAction(this); QAction *pToolBarUndo = mUndoStack.createUndoAction(this);
pToolBarUndo->setIcon(QIcon(":/icons/Undo.png")); pToolBarUndo->setIcon(QIcon(":/icons/Undo.png"));
@ -93,6 +96,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double))); connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double)));
connect(ui->ActionLink, SIGNAL(toggled(bool)), this, SLOT(OnLinkButtonToggled(bool))); connect(ui->ActionLink, SIGNAL(toggled(bool)), this, SLOT(OnLinkButtonToggled(bool)));
connect(ui->ActionUnlink, SIGNAL(triggered()), this, SLOT(OnUnlinkClicked())); connect(ui->ActionUnlink, SIGNAL(triggered()), this, SLOT(OnUnlinkClicked()));
connect(ui->ActionDelete, SIGNAL(triggered()), this, SLOT(DeleteSelection()));
connect(&mUndoStack, SIGNAL(indexChanged(int)), this, SLOT(OnUndoStackIndexChanged())); connect(&mUndoStack, SIGNAL(indexChanged(int)), this, SLOT(OnUndoStackIndexChanged()));
connect(ui->ActionSave, SIGNAL(triggered()), this, SLOT(Save())); connect(ui->ActionSave, SIGNAL(triggered()), this, SLOT(Save()));
@ -100,6 +104,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
ui->CreateTabEditorProperties->SyncToEditor(this); ui->CreateTabEditorProperties->SyncToEditor(this);
ui->ModifyTabEditorProperties->SyncToEditor(this); ui->ModifyTabEditorProperties->SyncToEditor(this);
ui->InstancesTabEditorProperties->SyncToEditor(this); ui->InstancesTabEditorProperties->SyncToEditor(this);
ui->MainViewport->setAcceptDrops(true);
} }
CWorldEditor::~CWorldEditor() CWorldEditor::~CWorldEditor()
@ -127,12 +132,7 @@ void CWorldEditor::closeEvent(QCloseEvent *pEvent)
} }
} }
bool CWorldEditor::eventFilter(QObject * /*pObj*/, QEvent * /*pEvent*/) void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea)
{
return false;
}
void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex)
{ {
ExitPickMode(); ExitPickMode();
ui->MainViewport->ResetHover(); ui->MainViewport->ResetHover();
@ -181,6 +181,7 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex)
// Set up sidebar tabs // Set up sidebar tabs
CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version()); CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version());
ui->CreateTabContents->SetMaster(pMaster);
ui->InstancesTabContents->SetMaster(pMaster); ui->InstancesTabContents->SetMaster(pMaster);
// Set up dialogs // Set up dialogs
@ -192,8 +193,8 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex)
if (CurrentGame() < eReturns) if (CurrentGame() < eReturns)
{ {
CStringTable *pAreaNameTable = mpWorld->GetAreaName(AreaIndex); CStringTable *pAreaNameTable = mpWorld->GetAreaName(mpArea->WorldIndex());
TWideString AreaName = pAreaNameTable ? pAreaNameTable->GetString("ENGL", 0) : (TWideString("!") + mpWorld->GetAreaInternalName(AreaIndex).ToUTF16()); TWideString AreaName = pAreaNameTable ? pAreaNameTable->GetString("ENGL", 0) : (TWideString("!") + mpWorld->GetAreaInternalName(mpArea->WorldIndex()).ToUTF16());
if (AreaName.IsEmpty()) if (AreaName.IsEmpty())
AreaName = "[Untitled Area]"; AreaName = "[Untitled Area]";
@ -206,7 +207,7 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex)
{ {
QString LevelName; QString LevelName;
if (pWorldNameTable) LevelName = TO_QSTRING(WorldName); if (pWorldNameTable) LevelName = TO_QSTRING(WorldName);
else LevelName = "!" + TO_QSTRING(mpWorld->GetAreaInternalName(AreaIndex)); else LevelName = "!" + TO_QSTRING(mpWorld->GetAreaInternalName(mpArea->WorldIndex()));
setWindowTitle(QString("Prime World Editor - %1[*]").arg(LevelName)); setWindowTitle(QString("Prime World Editor - %1[*]").arg(LevelName));
Log::Write("Loaded level: World " + mpWorld->Source() + " / Area " + mpArea->Source() + " (" + TO_TSTRING(LevelName) + ")"); Log::Write("Loaded level: World " + mpWorld->Source() + " / Area " + mpArea->Source() + " (" + TO_TSTRING(LevelName) + ")");
@ -238,7 +239,20 @@ bool CWorldEditor::CheckUnsavedChanges()
return OkToClear; return OkToClear;
} }
CSceneViewport* CWorldEditor::Viewport() const
{
return ui->MainViewport;
}
// ************ PUBLIC SLOTS ************ // ************ PUBLIC SLOTS ************
void CWorldEditor::NotifyNodeAboutToBeDeleted(CSceneNode *pNode)
{
INodeEditor::NotifyNodeAboutToBeDeleted(pNode);
if (ui->MainViewport->HoverNode() == pNode)
ui->MainViewport->ResetHover();
}
bool CWorldEditor::Save() bool CWorldEditor::Save()
{ {
TString Out = mpArea->FullSource(); TString Out = mpArea->FullSource();
@ -273,9 +287,9 @@ void CWorldEditor::OnPropertyModified(IProperty *pProp)
{ {
bool EditorProperty = false; bool EditorProperty = false;
if (!mSelection.isEmpty() && mSelection.front()->NodeType() == eScriptNode) if (!mpSelection->IsEmpty() && mpSelection->Front()->NodeType() == eScriptNode)
{ {
CScriptNode *pScript = static_cast<CScriptNode*>(mSelection.front()); CScriptNode *pScript = static_cast<CScriptNode*>(mpSelection->Front());
pScript->PropertyModified(pProp); pScript->PropertyModified(pProp);
// Check editor property // Check editor property
@ -295,13 +309,13 @@ void CWorldEditor::OnPropertyModified(IProperty *pProp)
CFileTemplate *pFile = static_cast<CFileTemplate*>(pProp->Template()); CFileTemplate *pFile = static_cast<CFileTemplate*>(pProp->Template());
if (pFile->AcceptsExtension("CMDL") || pFile->AcceptsExtension("ANCS") || pFile->AcceptsExtension("CHAR")) if (pFile->AcceptsExtension("CMDL") || pFile->AcceptsExtension("ANCS") || pFile->AcceptsExtension("CHAR"))
NotifySelectionModified(); SelectionModified();
} }
else if (pProp->Type() == eCharacterProperty) else if (pProp->Type() == eCharacterProperty)
NotifySelectionModified(); SelectionModified();
// Emit signal so other widgets can react to the property change // Emit signal so other widgets can react to the property change
emit PropertyModified(pProp, EditorProperty); emit PropertyModified(pScript->Object(), pProp);
} }
} }
@ -310,11 +324,11 @@ void CWorldEditor::SetSelectionActive(bool Active)
// Gather list of selected objects that actually have Active properties // Gather list of selected objects that actually have Active properties
QVector<CScriptObject*> Objects; QVector<CScriptObject*> Objects;
foreach (CSceneNode *pNode, mSelection) for (CSelectionIterator It(mpSelection); It; ++It)
{ {
if (pNode->NodeType() == eScriptNode) if (It->NodeType() == eScriptNode)
{ {
CScriptNode *pScript = static_cast<CScriptNode*>(pNode); CScriptNode *pScript = static_cast<CScriptNode*>(*It);
CScriptObject *pInst = pScript->Object(); CScriptObject *pInst = pScript->Object();
IProperty *pActive = pInst->ActiveProperty(); IProperty *pActive = pInst->ActiveProperty();
@ -343,9 +357,9 @@ void CWorldEditor::SetSelectionInstanceNames(const QString& rkNewName, bool IsDo
{ {
// todo: this only supports one node at a time because a macro prevents us from merging undo commands // todo: this only supports one node at a time because a macro prevents us from merging undo commands
// this is fine right now because this function is only ever called with a selection of one node, but probably want to fix in the future // this is fine right now because this function is only ever called with a selection of one node, but probably want to fix in the future
if (mSelection.size() == 1 && mSelection.front()->NodeType() == eScriptNode) if (mpSelection->Size() == 1 && mpSelection->Front()->NodeType() == eScriptNode)
{ {
CScriptNode *pNode = static_cast<CScriptNode*>(mSelection.front()); CScriptNode *pNode = static_cast<CScriptNode*>(mpSelection->Front());
CScriptObject *pInst = pNode->Object(); CScriptObject *pInst = pNode->Object();
IProperty *pName = pInst->InstanceNameProperty(); IProperty *pName = pInst->InstanceNameProperty();
@ -363,16 +377,23 @@ void CWorldEditor::SetSelectionLayer(CScriptLayer *pLayer)
{ {
QList<CScriptNode*> ScriptNodes; QList<CScriptNode*> ScriptNodes;
foreach (CSceneNode *pNode, mSelection) for (CSelectionIterator It(mpSelection); It; ++It)
{ {
if (pNode->NodeType() == eScriptNode) if (It->NodeType() == eScriptNode)
ScriptNodes << static_cast<CScriptNode*>(pNode); ScriptNodes << static_cast<CScriptNode*>(*It);
} }
if (!ScriptNodes.isEmpty()) if (!ScriptNodes.isEmpty())
mUndoStack.push(new CChangeLayerCommand(this, ScriptNodes, pLayer)); mUndoStack.push(new CChangeLayerCommand(this, ScriptNodes, pLayer));
} }
void CWorldEditor::DeleteSelection()
{
// note: make it only happen if there is a script node selected
CDeleteSelectionCommand *pCmd = new CDeleteSelectionCommand(this);
mUndoStack.push(pCmd);
}
void CWorldEditor::UpdateStatusBar() void CWorldEditor::UpdateStatusBar()
{ {
// Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag. // Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag.
@ -384,7 +405,7 @@ void CWorldEditor::UpdateStatusBar()
{ {
CSceneNode *pHoverNode = ui->MainViewport->HoverNode(); CSceneNode *pHoverNode = ui->MainViewport->HoverNode();
if (pHoverNode && (pHoverNode->NodeType() & mSelectionNodeFlags)) if (pHoverNode && mpSelection->IsAllowedType(pHoverNode))
StatusText = TO_QSTRING(pHoverNode->Name()); StatusText = TO_QSTRING(pHoverNode->Name());
} }
} }
@ -409,26 +430,26 @@ void CWorldEditor::UpdateGizmoUI()
case CGizmo::eTranslate: case CGizmo::eTranslate:
if (mGizmoTransforming && mGizmo.HasTransformed()) if (mGizmoTransforming && mGizmo.HasTransformed())
spinBoxValue = mGizmo.TotalTranslation(); spinBoxValue = mGizmo.TotalTranslation();
else if (!mSelection.empty()) else if (!mpSelection->IsEmpty())
spinBoxValue = mSelection.front()->AbsolutePosition(); spinBoxValue = mpSelection->Front()->AbsolutePosition();
break; break;
case CGizmo::eRotate: case CGizmo::eRotate:
if (mGizmoTransforming && mGizmo.HasTransformed()) if (mGizmoTransforming && mGizmo.HasTransformed())
spinBoxValue = mGizmo.TotalRotation(); spinBoxValue = mGizmo.TotalRotation();
else if (!mSelection.empty()) else if (!mpSelection->IsEmpty())
spinBoxValue = mSelection.front()->AbsoluteRotation().ToEuler(); spinBoxValue = mpSelection->Front()->AbsoluteRotation().ToEuler();
break; break;
case CGizmo::eScale: case CGizmo::eScale:
if (mGizmoTransforming && mGizmo.HasTransformed()) if (mGizmoTransforming && mGizmo.HasTransformed())
spinBoxValue = mGizmo.TotalScale(); spinBoxValue = mGizmo.TotalScale();
else if (!mSelection.empty()) else if (!mpSelection->IsEmpty())
spinBoxValue = mSelection.front()->AbsoluteScale(); spinBoxValue = mpSelection->Front()->AbsoluteScale();
break; break;
} }
} }
else if (!mSelection.empty()) spinBoxValue = mSelection.front()->AbsolutePosition(); else if (!mpSelection->IsEmpty()) spinBoxValue = mpSelection->Front()->AbsolutePosition();
ui->TransformSpinBox->blockSignals(true); ui->TransformSpinBox->blockSignals(true);
ui->TransformSpinBox->SetValue(spinBoxValue); ui->TransformSpinBox->SetValue(spinBoxValue);
@ -439,26 +460,23 @@ void CWorldEditor::UpdateGizmoUI()
if (!mGizmoTransforming) if (!mGizmoTransforming)
{ {
// Set gizmo transform // Set gizmo transform
if (!mSelection.empty()) if (!mpSelection->IsEmpty())
{ {
mGizmo.SetPosition(mSelection.front()->AbsolutePosition()); mGizmo.SetPosition(mpSelection->Front()->AbsolutePosition());
mGizmo.SetLocalRotation(mSelection.front()->AbsoluteRotation()); mGizmo.SetLocalRotation(mpSelection->Front()->AbsoluteRotation());
} }
} }
} }
void CWorldEditor::UpdateSelectionUI() void CWorldEditor::UpdateSelectionUI()
{ {
// Update sidebar
ui->ModifyTabContents->GenerateUI(mSelection);
// Update selection info text // Update selection info text
QString SelectionText; QString SelectionText;
if (mSelection.size() == 1) if (mpSelection->Size() == 1)
SelectionText = TO_QSTRING(mSelection.front()->Name()); SelectionText = TO_QSTRING(mpSelection->Front()->Name());
else if (mSelection.size() > 1) else if (mpSelection->Size() > 1)
SelectionText = QString("%1 objects selected").arg(mSelection.size()); SelectionText = QString("%1 objects selected").arg(mpSelection->Size());
QFontMetrics Metrics(ui->SelectionInfoLabel->font()); QFontMetrics Metrics(ui->SelectionInfoLabel->font());
SelectionText = Metrics.elidedText(SelectionText, Qt::ElideRight, ui->SelectionInfoFrame->width() - 10); SelectionText = Metrics.elidedText(SelectionText, Qt::ElideRight, ui->SelectionInfoFrame->width() - 10);
@ -478,7 +496,7 @@ void CWorldEditor::UpdateCursor()
if (ui->MainViewport->IsHoveringGizmo()) if (ui->MainViewport->IsHoveringGizmo())
ui->MainViewport->SetCursorState(Qt::SizeAllCursor); ui->MainViewport->SetCursorState(Qt::SizeAllCursor);
else if ((pHoverNode) && (pHoverNode->NodeType() & mSelectionNodeFlags)) else if ((pHoverNode) && mpSelection->IsAllowedType(pHoverNode))
ui->MainViewport->SetCursorState(Qt::PointingHandCursor); ui->MainViewport->SetCursorState(Qt::PointingHandCursor);
else else
ui->MainViewport->SetCursorState(Qt::ArrowCursor); ui->MainViewport->SetCursorState(Qt::ArrowCursor);
@ -604,10 +622,10 @@ void CWorldEditor::OnUnlinkClicked()
{ {
QList<CScriptNode*> SelectedScriptNodes; QList<CScriptNode*> SelectedScriptNodes;
foreach (CSceneNode *pNode, mSelection) for (CSelectionIterator It(mpSelection); It; ++It)
{ {
if (pNode->NodeType() == eScriptNode) if (It->NodeType() == eScriptNode)
SelectedScriptNodes << static_cast<CScriptNode*>(pNode); SelectedScriptNodes << static_cast<CScriptNode*>(*It);
} }
if (!SelectedScriptNodes.isEmpty()) if (!SelectedScriptNodes.isEmpty())
@ -727,8 +745,8 @@ void CWorldEditor::UpdateCameraOrbit()
{ {
CCamera *pCamera = &ui->MainViewport->Camera(); CCamera *pCamera = &ui->MainViewport->Camera();
if (!mSelection.isEmpty()) if (!mpSelection->IsEmpty())
pCamera->SetOrbit(mSelectionBounds); pCamera->SetOrbit(mpSelection->Bounds());
else if (mpArea) else if (mpArea)
pCamera->SetOrbit(mpArea->AABox(), 1.5f); pCamera->SetOrbit(mpArea->AABox(), 1.5f);
} }
@ -745,34 +763,33 @@ void CWorldEditor::OnCameraSpeedChange(double speed)
void CWorldEditor::OnTransformSpinBoxModified(CVector3f value) void CWorldEditor::OnTransformSpinBoxModified(CVector3f value)
{ {
if (mSelection.empty()) return; if (mpSelection->IsEmpty()) return;
switch (mGizmo.Mode()) switch (mGizmo.Mode())
{ {
// Use absolute position/rotation, but relative scale. (This way spinbox doesn't show preview multiplier) // Use absolute position/rotation, but relative scale. (This way spinbox doesn't show preview multiplier)
case CGizmo::eTranslate: case CGizmo::eTranslate:
{ {
CVector3f delta = value - mSelection.front()->AbsolutePosition(); CVector3f delta = value - mpSelection->Front()->AbsolutePosition();
mUndoStack.push(new CTranslateNodeCommand(this, mSelection, delta, mTranslateSpace)); mUndoStack.push(new CTranslateNodeCommand(this, mpSelection->SelectedNodeList(), delta, mTranslateSpace));
break; break;
} }
case CGizmo::eRotate: case CGizmo::eRotate:
{ {
CQuaternion delta = CQuaternion::FromEuler(value) * mSelection.front()->AbsoluteRotation().Inverse(); CQuaternion delta = CQuaternion::FromEuler(value) * mpSelection->Front()->AbsoluteRotation().Inverse();
mUndoStack.push(new CRotateNodeCommand(this, mSelection, CVector3f::skZero, delta, mRotateSpace)); mUndoStack.push(new CRotateNodeCommand(this, mpSelection->SelectedNodeList(), CVector3f::skZero, delta, mRotateSpace));
break; break;
} }
case CGizmo::eScale: case CGizmo::eScale:
{ {
CVector3f delta = value / mSelection.front()->AbsoluteScale(); CVector3f delta = value / mpSelection->Front()->AbsoluteScale();
mUndoStack.push(new CScaleNodeCommand(this, mSelection, CVector3f::skZero, delta)); mUndoStack.push(new CScaleNodeCommand(this, mpSelection->SelectedNodeList(), CVector3f::skZero, delta));
break; break;
} }
} }
RecalculateSelectionBounds();
UpdateGizmoUI(); UpdateGizmoUI();
} }
@ -782,7 +799,7 @@ void CWorldEditor::OnTransformSpinBoxEdited(CVector3f)
ui->TransformSpinBox->blockSignals(true); ui->TransformSpinBox->blockSignals(true);
ui->MainViewport->setFocus(); ui->MainViewport->setFocus();
ui->TransformSpinBox->blockSignals(false); ui->TransformSpinBox->blockSignals(false);
if (mSelection.empty()) return; if (mpSelection->IsEmpty()) return;
if (mGizmo.Mode() == CGizmo::eTranslate) mUndoStack.push(CTranslateNodeCommand::End()); if (mGizmo.Mode() == CGizmo::eTranslate) mUndoStack.push(CTranslateNodeCommand::End());
else if (mGizmo.Mode() == CGizmo::eRotate) mUndoStack.push(CRotateNodeCommand::End()); else if (mGizmo.Mode() == CGizmo::eRotate) mUndoStack.push(CRotateNodeCommand::End());

View File

@ -5,6 +5,7 @@
#include "CPoiMapEditDialog.h" #include "CPoiMapEditDialog.h"
#include "Editor/INodeEditor.h" #include "Editor/INodeEditor.h"
#include "Editor/CGizmo.h" #include "Editor/CGizmo.h"
#include "Editor/CSceneViewport.h"
#include <Common/CTimer.h> #include <Common/CTimer.h>
#include <Common/EKeyInputs.h> #include <Common/EKeyInputs.h>
@ -47,21 +48,24 @@ public:
explicit CWorldEditor(QWidget *parent = 0); explicit CWorldEditor(QWidget *parent = 0);
~CWorldEditor(); ~CWorldEditor();
void closeEvent(QCloseEvent *pEvent); void closeEvent(QCloseEvent *pEvent);
bool eventFilter(QObject *pObj, QEvent *pEvent); void SetArea(CWorld *pWorld, CGameArea *pArea);
void SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex);
bool CheckUnsavedChanges(); bool CheckUnsavedChanges();
inline CGameArea* ActiveArea() const { return mpArea; } inline CGameArea* ActiveArea() const { return mpArea; }
inline EGame CurrentGame() const { return mpArea ? mpArea->Version() : eUnknownVersion; } inline EGame CurrentGame() const { return mpArea ? mpArea->Version() : eUnknownVersion; }
inline CLinkDialog* LinkDialog() const { return mpLinkDialog; } inline CLinkDialog* LinkDialog() const { return mpLinkDialog; }
CSceneViewport* Viewport() const;
public slots: public slots:
virtual void NotifyNodeAboutToBeDeleted(CSceneNode *pNode);
bool Save(); bool Save();
void OnLinksModified(const QList<CScriptObject*>& rkInstances); void OnLinksModified(const QList<CScriptObject*>& rkInstances);
void OnPropertyModified(IProperty *pProp); void OnPropertyModified(IProperty *pProp);
void SetSelectionActive(bool Active); void SetSelectionActive(bool Active);
void SetSelectionInstanceNames(const QString& rkNewName, bool IsDone); void SetSelectionInstanceNames(const QString& rkNewName, bool IsDone);
void SetSelectionLayer(CScriptLayer *pLayer); void SetSelectionLayer(CScriptLayer *pLayer);
void DeleteSelection();
void UpdateStatusBar(); void UpdateStatusBar();
void UpdateGizmoUI(); void UpdateGizmoUI();
@ -116,7 +120,7 @@ signals:
void InstancesLayerAboutToChange(); void InstancesLayerAboutToChange();
void InstancesLayerChanged(const QList<CScriptNode*>& rkInstanceList); void InstancesLayerChanged(const QList<CScriptNode*>& rkInstanceList);
void InstanceLinksModified(const QList<CScriptObject*>& rkInstances); void InstanceLinksModified(const QList<CScriptObject*>& rkInstances);
void PropertyModified(IProperty *pProp, bool IsEditorProperty); void PropertyModified(CScriptObject *pInst, IProperty *pProp);
}; };
#endif // CWORLDEDITOR_H #endif // CWORLDEDITOR_H

View File

@ -229,7 +229,7 @@
<enum>QTabWidget::Rounded</enum> <enum>QTabWidget::Rounded</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<property name="iconSize"> <property name="iconSize">
<size> <size>
@ -268,17 +268,14 @@
<widget class="WEditorProperties" name="CreateTabEditorProperties" native="true"/> <widget class="WEditorProperties" name="CreateTabEditorProperties" native="true"/>
</item> </item>
<item> <item>
<spacer name="verticalSpacer"> <widget class="WCreateTab" name="CreateTabContents" native="true">
<property name="orientation"> <property name="sizePolicy">
<enum>Qt::Vertical</enum> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property> </property>
<property name="sizeHint" stdset="0"> </widget>
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -755,6 +752,14 @@
<string>Edit POI to World Map</string> <string>Edit POI to World Map</string>
</property> </property>
</action> </action>
<action name="ActionDelete">
<property name="text">
<string>Delete</string>
</property>
<property name="shortcut">
<string>Del</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
@ -792,6 +797,12 @@
<header>Editor/WorldEditor/WEditorProperties.h</header> <header>Editor/WorldEditor/WEditorProperties.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>WCreateTab</class>
<extends>QWidget</extends>
<header>Editor/WorldEditor/WCreateTab.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="../Icons.qrc"/> <include location="../Icons.qrc"/>

View File

@ -1,5 +1,8 @@
#include "WCreateTab.h" #include "WCreateTab.h"
#include "ui_WCreateTab.h" #include "ui_WCreateTab.h"
#include "CTemplateMimeData.h"
#include "CWorldEditor.h"
#include "Editor/Undo/UndoCommands.h"
WCreateTab::WCreateTab(QWidget *parent) : WCreateTab::WCreateTab(QWidget *parent) :
QWidget(parent), QWidget(parent),
@ -12,3 +15,47 @@ WCreateTab::~WCreateTab()
{ {
delete ui; delete ui;
} }
bool WCreateTab::eventFilter(QObject *pObj, QEvent *pEvent)
{
if (pObj == mpEditor->Viewport())
{
if (pEvent->type() == QEvent::DragEnter)
{
QDragEnterEvent *pDragEvent = static_cast<QDragEnterEvent*>(pEvent);
if (qobject_cast<const CTemplateMimeData*>(pDragEvent->mimeData()))
{
pDragEvent->acceptProposedAction();
return true;
}
}
else if (pEvent->type() == QEvent::Drop)
{
QDropEvent *pDropEvent = static_cast<QDropEvent*>(pEvent);
const CTemplateMimeData *pMimeData = qobject_cast<const CTemplateMimeData*>(pDropEvent->mimeData());
if (pMimeData)
{
CVector3f SpawnPoint = mpEditor->Viewport()->HoverPoint();
CCreateInstanceCommand *pCmd = new CCreateInstanceCommand(mpEditor, pMimeData->Template(), mpEditor->ActiveArea()->GetScriptLayer(0), SpawnPoint);
mpEditor->UndoStack()->push(pCmd);
return true;
}
}
}
return false;
}
void WCreateTab::SetEditor(CWorldEditor *pEditor)
{
mpEditor = pEditor;
pEditor->Viewport()->installEventFilter(this);
}
void WCreateTab::SetMaster(CMasterTemplate *pMaster)
{
ui->TemplateView->SetMaster(pMaster);
}

View File

@ -1,6 +1,8 @@
#ifndef WCREATETAB_H #ifndef WCREATETAB_H
#define WCREATETAB_H #define WCREATETAB_H
#include "CWorldEditor.h"
#include <Core/Resource/Script/CMasterTemplate.h>
#include <QWidget> #include <QWidget>
namespace Ui { namespace Ui {
@ -10,11 +12,14 @@ class WCreateTab;
class WCreateTab : public QWidget class WCreateTab : public QWidget
{ {
Q_OBJECT Q_OBJECT
CWorldEditor *mpEditor;
public: public:
explicit WCreateTab(QWidget *parent = 0); explicit WCreateTab(QWidget *parent = 0);
~WCreateTab(); ~WCreateTab();
bool eventFilter(QObject *, QEvent *);
void SetEditor(CWorldEditor *pEditor);
void SetMaster(CMasterTemplate *pMaster);
private: private:
Ui::WCreateTab *ui; Ui::WCreateTab *ui;
}; };

View File

@ -6,14 +6,90 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>216</width> <width>290</width>
<height>421</height> <height>470</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Create Instance</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Spawn Layer:</string>
</property>
</widget> </widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="CTemplateListView" name="TemplateView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CTemplateListView</class>
<extends>QListView</extends>
<header>Editor/WorldEditor/CTemplateListView.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -65,7 +65,7 @@ void WEditorProperties::SyncToEditor(CWorldEditor *pEditor)
connect(mpEditor, SIGNAL(SelectionModified()), this, SLOT(OnSelectionModified())); connect(mpEditor, SIGNAL(SelectionModified()), this, SLOT(OnSelectionModified()));
connect(mpEditor, SIGNAL(LayersModified()), this, SLOT(OnLayersModified())); connect(mpEditor, SIGNAL(LayersModified()), this, SLOT(OnLayersModified()));
connect(mpEditor, SIGNAL(InstancesLayerChanged(QList<CScriptNode*>)), this, SLOT(OnInstancesLayerChanged(QList<CScriptNode*>))); connect(mpEditor, SIGNAL(InstancesLayerChanged(QList<CScriptNode*>)), this, SLOT(OnInstancesLayerChanged(QList<CScriptNode*>)));
connect(mpEditor, SIGNAL(PropertyModified(IProperty*,bool)), this, SLOT(OnPropertyModified(IProperty*,bool))); connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), this, SLOT(OnPropertyModified(CScriptObject*,IProperty*)));
OnLayersModified(); OnLayersModified();
} }
@ -100,16 +100,16 @@ void WEditorProperties::SetLayerComboBox()
// ************ PUBLIC SLOTS ************ // ************ PUBLIC SLOTS ************
void WEditorProperties::OnSelectionModified() void WEditorProperties::OnSelectionModified()
{ {
const QList<CSceneNode*>& rkSelection = mpEditor->GetSelection(); CNodeSelection *pSelection = mpEditor->Selection();
mpDisplayNode = (rkSelection.size() == 1 ? rkSelection.front() : nullptr); mpDisplayNode = (pSelection->Size() == 1 ? pSelection->Front() : nullptr);
if (rkSelection.empty() || rkSelection.size() != 1 || mpDisplayNode->NodeType() != eScriptNode) if (pSelection->IsEmpty() || pSelection->Size() != 1 || mpDisplayNode->NodeType() != eScriptNode)
{ {
mpActiveCheckBox->setChecked(false); mpActiveCheckBox->setChecked(false);
mpActiveCheckBox->setEnabled(false); mpActiveCheckBox->setEnabled(false);
mpInstanceNameLineEdit->setEnabled(false); mpInstanceNameLineEdit->setEnabled(false);
if (rkSelection.empty()) if (pSelection->IsEmpty())
{ {
mpInstanceInfoLabel->setText("<i>[No selection]</i>"); mpInstanceInfoLabel->setText("<i>[No selection]</i>");
mpInstanceNameLineEdit->clear(); mpInstanceNameLineEdit->clear();
@ -121,7 +121,7 @@ void WEditorProperties::OnSelectionModified()
} }
else else
{ {
mpInstanceInfoLabel->setText(QString("<i>[%1 objects selected]</i>").arg(rkSelection.size())); mpInstanceInfoLabel->setText(QString("<i>[%1 objects selected]</i>").arg(pSelection->Size()));
mpInstanceNameLineEdit->clear(); mpInstanceNameLineEdit->clear();
} }
} }
@ -139,11 +139,11 @@ void WEditorProperties::OnSelectionModified()
SetLayerComboBox(); SetLayerComboBox();
} }
void WEditorProperties::OnPropertyModified(IProperty* /*pProp*/, bool IsEditorProperty) void WEditorProperties::OnPropertyModified(CScriptObject *pInstance, IProperty *pProp)
{ {
if (!mpInstanceNameLineEdit->hasFocus()) if (!mpInstanceNameLineEdit->hasFocus())
{ {
if (mpDisplayNode->NodeType() == eScriptNode && IsEditorProperty) if (mpDisplayNode->NodeType() == eScriptNode && pInstance->IsEditorProperty(pProp))
UpdatePropertyValues(); UpdatePropertyValues();
} }
} }

View File

@ -39,7 +39,7 @@ public:
public slots: public slots:
void OnSelectionModified(); void OnSelectionModified();
void OnPropertyModified(IProperty *pProp, bool IsEditorProperty); void OnPropertyModified(CScriptObject *pInst, IProperty *pProp);
void OnInstancesLayerChanged(const QList<CScriptNode*>& rkNodeList); void OnInstancesLayerChanged(const QList<CScriptNode*>& rkNodeList);
void OnLayersModified(); void OnLayersModified();
void UpdatePropertyValues(); void UpdatePropertyValues();

View File

@ -64,18 +64,28 @@ void WModifyTab::SetEditor(CWorldEditor *pEditor)
ui->PropertyView->SetEditor(mpWorldEditor); ui->PropertyView->SetEditor(mpWorldEditor);
connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed())); connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed()));
connect(mpWorldEditor, SIGNAL(InstanceLinksModified(const QList<CScriptObject*>&)), this, SLOT(OnInstanceLinksModified(const QList<CScriptObject*>&))); connect(mpWorldEditor, SIGNAL(InstanceLinksModified(const QList<CScriptObject*>&)), this, SLOT(OnInstanceLinksModified(const QList<CScriptObject*>&)));
connect(mpWorldEditor->Selection(), SIGNAL(Modified()), this, SLOT(GenerateUI()));
} }
void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection) void WModifyTab::ClearUI()
{
ui->ObjectsTabWidget->hide();
ui->PropertyView->SetInstance(nullptr);
ui->LightGroupBox->hide();
mpSelectedNode = nullptr;
}
// ************ PUBLIC SLOTS ************
void WModifyTab::GenerateUI()
{ {
if (mIsPicking) if (mIsPicking)
mpWorldEditor->ExitPickMode(); mpWorldEditor->ExitPickMode();
if (Selection.size() == 1) if (mpWorldEditor->Selection()->Size() == 1)
{ {
if (mpSelectedNode != Selection.front()) if (mpSelectedNode != mpWorldEditor->Selection()->Front())
{ {
mpSelectedNode = Selection.front(); mpSelectedNode = mpWorldEditor->Selection()->Front();
// todo: set up editing UI for Light Nodes // todo: set up editing UI for Light Nodes
if (mpSelectedNode->NodeType() == eScriptNode) if (mpSelectedNode->NodeType() == eScriptNode)
@ -100,15 +110,6 @@ void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection)
ClearUI(); ClearUI();
} }
void WModifyTab::ClearUI()
{
ui->ObjectsTabWidget->hide();
ui->PropertyView->SetInstance(nullptr);
ui->LightGroupBox->hide();
mpSelectedNode = nullptr;
}
// ************ PUBLIC SLOTS ************
void WModifyTab::OnInstanceLinksModified(const QList<CScriptObject*>& rkInstances) void WModifyTab::OnInstanceLinksModified(const QList<CScriptObject*>& rkInstances)
{ {
if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)

View File

@ -3,6 +3,7 @@
#include "CLinkDialog.h" #include "CLinkDialog.h"
#include "CLinkModel.h" #include "CLinkModel.h"
#include "Editor/CNodeSelection.h"
#include <Core/Scene/CSceneNode.h> #include <Core/Scene/CSceneNode.h>
#include <Core/Scene/CScriptNode.h> #include <Core/Scene/CScriptNode.h>
@ -36,10 +37,10 @@ public:
explicit WModifyTab(QWidget *pParent = 0); explicit WModifyTab(QWidget *pParent = 0);
~WModifyTab(); ~WModifyTab();
void SetEditor(CWorldEditor *pEditor); void SetEditor(CWorldEditor *pEditor);
void GenerateUI(QList<CSceneNode*>& Selection);
void ClearUI(); void ClearUI();
public slots: public slots:
void GenerateUI();
void OnInstanceLinksModified(const QList<CScriptObject*>& rkInstances); void OnInstanceLinksModified(const QList<CScriptObject*>& rkInstances);
void OnWorldSelectionTransformed(); void OnWorldSelectionTransformed();

View File

@ -20,22 +20,22 @@ CQuaternion::CQuaternion(float _w, float _x, float _y, float _z)
z = _z; z = _z;
} }
CVector3f CQuaternion::XAxis() CVector3f CQuaternion::XAxis() const
{ {
return (*this * CVector3f::skUnitX); return (*this * CVector3f::skUnitX);
} }
CVector3f CQuaternion::YAxis() CVector3f CQuaternion::YAxis() const
{ {
return (*this * CVector3f::skUnitY); return (*this * CVector3f::skUnitY);
} }
CVector3f CQuaternion::ZAxis() CVector3f CQuaternion::ZAxis() const
{ {
return (*this * CVector3f::skUnitZ); return (*this * CVector3f::skUnitZ);
} }
CQuaternion CQuaternion::Inverse() CQuaternion CQuaternion::Inverse() const
{ {
float fNorm = (w * w) + (x * x) + (y * y) + (z * z); float fNorm = (w * w) + (x * x) + (y * y) + (z * z);
@ -48,7 +48,7 @@ CQuaternion CQuaternion::Inverse()
return CQuaternion::skZero; return CQuaternion::skZero;
} }
CVector3f CQuaternion::ToEuler() CVector3f CQuaternion::ToEuler() const
{ {
// There is more than one way to do this conversion, based on rotation order. // There is more than one way to do this conversion, based on rotation order.
// But since we only care about the rotation order used in Retro games, which is consistent, // But since we only care about the rotation order used in Retro games, which is consistent,

View File

@ -11,11 +11,11 @@ public:
CQuaternion(); CQuaternion();
CQuaternion(float _w, float _x, float _y, float _z); CQuaternion(float _w, float _x, float _y, float _z);
CVector3f XAxis(); CVector3f XAxis() const;
CVector3f YAxis(); CVector3f YAxis() const;
CVector3f ZAxis(); CVector3f ZAxis() const;
CQuaternion Inverse(); CQuaternion Inverse() const;
CVector3f ToEuler(); CVector3f ToEuler() const;
// Operators // Operators
CVector3f operator*(const CVector3f& vec) const; CVector3f operator*(const CVector3f& vec) const;