diff --git a/src/Common/Log.cpp b/src/Common/Log.cpp index 8455bdaf..cbeeacd9 100644 --- a/src/Common/Log.cpp +++ b/src/Common/Log.cpp @@ -58,6 +58,8 @@ void Write(const TString& rkMessage) fprintf(gpLogFile, "[%08.3f] %s\n", Time, *rkMessage); fflush(gpLogFile); } + + std::cout << rkMessage << "\n"; } void Error(const TString& rkMessage) @@ -65,7 +67,6 @@ void Error(const TString& rkMessage) TString FullMessage = "ERROR: " + rkMessage; Write(FullMessage); gErrorLog.push_back(FullMessage); - std::cout << FullMessage << "\n"; } void Warning(const TString& rkMessage) @@ -73,7 +74,6 @@ void Warning(const TString& rkMessage) TString FullMessage = "Warning: " + rkMessage; Write(FullMessage); gErrorLog.push_back(FullMessage); - std::cout << FullMessage << "\n"; } void FileWrite(const TString& rkFilename, const TString& rkMessage) diff --git a/src/Core/CAreaAttributes.h b/src/Core/CAreaAttributes.h index dd6752e4..02890f20 100644 --- a/src/Core/CAreaAttributes.h +++ b/src/Core/CAreaAttributes.h @@ -15,6 +15,8 @@ public: bool IsLayerEnabled() const; bool IsSkyEnabled() const; CModel* SkyModel() const; + + inline CScriptObject* Instance() const { return mpObj; } }; #endif // CAREAATTRIBUTES_H diff --git a/src/Core/Resource/CGameArea.cpp b/src/Core/Resource/CGameArea.cpp index 047ee4fe..a3ddf0a9 100644 --- a/src/Core/Resource/CGameArea.cpp +++ b/src/Core/Resource/CGameArea.cpp @@ -2,23 +2,25 @@ #include "Core/Resource/Script/CScriptLayer.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() { ClearTerrain(); - delete mCollision; + delete mpCollision; delete mpGeneratorLayer; for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++) @@ -108,85 +110,109 @@ void CGameArea::ClearScriptLayers() 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() -{ - 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) +CScriptObject* CGameArea::InstanceByID(u32 InstanceID) { auto it = mObjectMap.find(InstanceID); if (it != mObjectMap.end()) return it->second; 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; - return mLightLayers[layer].size(); -} + pInstance->Layer()->RemoveInstance(pInstance); + pInstance->Template()->RemoveObject(pInstance); -CLight* CGameArea::GetLight(u32 layer, u32 light) -{ - return mLightLayers[layer][light]; -} + auto it = mObjectMap.find(pInstance->InstanceID()); + if (it != mObjectMap.end()) mObjectMap.erase(it); -CPoiToWorld* CGameArea::GetPoiToWorldMap() -{ - return mpPoiToWorldMap; -} + if (mpPoiToWorldMap && mpPoiToWorldMap->HasPoiMappings(pInstance->InstanceID())) + mpPoiToWorldMap->RemovePoi(pInstance->InstanceID()); -CAABox CGameArea::AABox() -{ - return mAABox; + pInstance->BreakAllLinks(); + delete pInstance; } diff --git a/src/Core/Resource/CGameArea.h b/src/Core/Resource/CGameArea.h index 81c68864..9fc863f3 100644 --- a/src/Core/Resource/CGameArea.h +++ b/src/Core/Resource/CGameArea.h @@ -9,12 +9,14 @@ #include "Core/Resource/Model/CModel.h" #include "Core/Resource/Model/CStaticModel.h" #include +#include #include #include class CScriptLayer; class CScriptObject; +class CScriptTemplate; class CGameArea : public CResource { @@ -23,6 +25,7 @@ class CGameArea : public CResource friend class CAreaCooker; EGame mVersion; + u32 mWorldIndex; u32 mVertexCount; u32 mTriangleCount; bool mTerrainMerged; @@ -50,7 +53,7 @@ class CGameArea : public CResource CScriptLayer *mpGeneratorLayer; std::unordered_map mObjectMap; // Collision - CCollisionMeshGroup *mCollision; + CCollisionMeshGroup *mpCollision; // Lights std::vector> mLightLayers; // Object to Static Geometry Map @@ -64,24 +67,34 @@ public: void MergeTerrain(); void ClearTerrain(); 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 - EGame Version(); - CTransform4f GetTransform(); - u32 GetTerrainModelCount(); - u32 GetStaticModelCount(); - CModel* GetTerrainModel(u32 mdl); - CStaticModel* GetStaticModel(u32 mdl); - CCollisionMeshGroup* GetCollision(); - u32 GetScriptLayerCount(); - CScriptLayer* GetScriptLayer(u32 index); - CScriptLayer* GetGeneratorLayer(); - CScriptObject* GetInstanceByID(u32 InstanceID); - u32 GetLightLayerCount(); - u32 GetLightCount(u32 layer); - CLight* GetLight(u32 layer, u32 light); - CPoiToWorld* GetPoiToWorldMap(); - CAABox AABox(); + // Inline Accessors + inline EGame Version() const { return mVersion; } + inline u32 WorldIndex() const { return mWorldIndex; } + inline CTransform4f GetTransform() const { return mTransform; } + inline u32 GetTerrainModelCount() const { return mTerrainModels.size(); } + inline u32 GetStaticModelCount() const { return mStaticTerrainModels.size(); } + inline CModel* GetTerrainModel(u32 iMdl) const { return mTerrainModels[iMdl]; } + inline CStaticModel* GetStaticModel(u32 iMdl) const { return mStaticTerrainModels[iMdl]; } + inline CCollisionMeshGroup* GetCollision() const { return mpCollision; } + inline u32 GetScriptLayerCount() const { return mScriptLayers.size(); } + inline CScriptLayer* GetScriptLayer(u32 Index) const { return mScriptLayers[Index]; } + inline CScriptLayer* GetGeneratorLayer() const { return mpGeneratorLayer; } + inline u32 GetLightLayerCount() const { return mLightLayers.size(); } + inline u32 GetLightCount(u32 LayerIndex) const { return (LayerIndex < mLightLayers.size() ? mLightLayers[LayerIndex].size() : 0); } + inline CLight* GetLight(u32 LayerIndex, u32 LightIndex) const { return mLightLayers[LayerIndex][LightIndex]; } + inline CPoiToWorld* GetPoiToWorldMap() const { return mpPoiToWorldMap; } + inline CAABox AABox() const { return mAABox; } + + inline void SetWorldIndex(u32 NewWorldIndex) { mWorldIndex = NewWorldIndex; } }; #endif // CGAMEAREA_H diff --git a/src/Core/Resource/CPoiToWorld.h b/src/Core/Resource/CPoiToWorld.h index f426cad6..8598c0f4 100644 --- a/src/Core/Resource/CPoiToWorld.h +++ b/src/Core/Resource/CPoiToWorld.h @@ -39,6 +39,22 @@ public: { 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 diff --git a/src/Core/Resource/CResourceInfo.h b/src/Core/Resource/CResourceInfo.h index 56b522a5..c61ac40d 100644 --- a/src/Core/Resource/CResourceInfo.h +++ b/src/Core/Resource/CResourceInfo.h @@ -31,7 +31,7 @@ public: inline CUniqueID ID() const { - TString FileName = mPath.GetFileName(); + TString FileName = mPath.GetFileName(false); if (!mIsPath) return CUniqueID::FromString(FileName); diff --git a/src/Core/Resource/CWorld.cpp b/src/Core/Resource/CWorld.cpp index 52366b3e..91fd5b1c 100644 --- a/src/Core/Resource/CWorld.cpp +++ b/src/Core/Resource/CWorld.cpp @@ -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. // 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. - SArea& AreaInfo = mAreas[AreaIndex]; + SArea& AreaInfo = mAreas[pArea->WorldIndex()]; for (u32 iLyr = 0; iLyr < pArea->GetScriptLayerCount(); iLyr++) { diff --git a/src/Core/Resource/CWorld.h b/src/Core/Resource/CWorld.h index cad2110b..0c703700 100644 --- a/src/Core/Resource/CWorld.h +++ b/src/Core/Resource/CWorld.h @@ -84,7 +84,7 @@ public: CWorld(); ~CWorld(); - void SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex); + void SetAreaLayerInfo(CGameArea *pArea); // Setters EGame Version(); diff --git a/src/Core/Resource/Factory/CAreaLoader.cpp b/src/Core/Resource/Factory/CAreaLoader.cpp index 2457df53..1ce905ee 100644 --- a/src/Core/Resource/Factory/CAreaLoader.cpp +++ b/src/Core/Resource/Factory/CAreaLoader.cpp @@ -595,7 +595,7 @@ void CAreaLoader::ReadCollision() { Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)"); mpSectionMgr->ToSection(mCollisionBlockNum); - mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA); + mpArea->mpCollision = CCollisionLoader::LoadAreaCollision(*mpMREA); } void CAreaLoader::ReadEGMC() @@ -643,7 +643,7 @@ void CAreaLoader::SetUpObjects() if (iConMap != mConnectionMap.end()) { - CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID); + CScriptObject *pObj = mpArea->InstanceByID(InstanceID); pObj->mInLinks = iConMap->second; } } diff --git a/src/Core/Resource/Factory/CScriptLoader.cpp b/src/Core/Resource/Factory/CScriptLoader.cpp index fed03cbb..29acb3c2 100644 --- a/src/Core/Resource/Factory/CScriptLoader.cpp +++ b/src/Core/Resource/Factory/CScriptLoader.cpp @@ -214,8 +214,8 @@ CScriptObject* CScriptLoader::LoadObjectMP1(IInputStream& SCLY) return nullptr; } - mpObj = new CScriptObject(mpArea, mpLayer, pTemp); - mpObj->mInstanceID = SCLY.ReadLong(); + u32 InstanceID = SCLY.ReadLong(); + mpObj = new CScriptObject(InstanceID, mpArea, mpLayer, pTemp); // Load connections 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 u32 NumObjects = SCLY.ReadLong(); - mpLayer = new CScriptLayer(); + mpLayer = new CScriptLayer(mpArea); mpLayer->Reserve(NumObjects); for (u32 iObj = 0; iObj < NumObjects; iObj++) @@ -327,8 +327,8 @@ CScriptObject* CScriptLoader::LoadObjectMP2(IInputStream& SCLY) return nullptr; } - mpObj = new CScriptObject(mpArea, mpLayer, pTemplate); - mpObj->mInstanceID = SCLY.ReadLong(); + u32 InstanceID = SCLY.ReadLong(); + mpObj = new CScriptObject(InstanceID, mpArea, mpLayer, pTemplate); // Load connections u32 NumConnections = SCLY.ReadShort(); @@ -359,7 +359,7 @@ CScriptLayer* CScriptLoader::LoadLayerMP2(IInputStream& SCLY) SCLY.Seek(0x1, SEEK_CUR); // Skipping version. todo: verify this? u32 NumObjects = SCLY.ReadLong(); - mpLayer = new CScriptLayer(); + mpLayer = new CScriptLayer(mpArea); mpLayer->Reserve(NumObjects); for (u32 iObj = 0; iObj < NumObjects; iObj++) @@ -372,9 +372,10 @@ CScriptLayer* CScriptLoader::LoadLayerMP2(IInputStream& SCLY) 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; Loader.mVersion = Version; @@ -391,7 +392,7 @@ CScriptLayer* CScriptLoader::LoadLayer(IInputStream &SCLY, CGameArea *pArea, EGa CTemplateLoader::LoadGameTemplates(Version); if (Version <= ePrime) - return Loader.LoadLayerMP1(SCLY); + return Loader.LoadLayerMP1(rSCLY); else - return Loader.LoadLayerMP2(SCLY); + return Loader.LoadLayerMP2(rSCLY); } diff --git a/src/Core/Resource/Script/CLink.h b/src/Core/Resource/Script/CLink.h index 3709e8bd..c1a4d12e 100644 --- a/src/Core/Resource/Script/CLink.h +++ b/src/Core/Resource/Script/CLink.h @@ -52,8 +52,8 @@ public: void SetSender(u32 NewSenderID, u32 Index = -1) { u32 OldSenderID = mSenderID; - CScriptObject *pOldSender = mpArea->GetInstanceByID(OldSenderID); - CScriptObject *pNewSender = mpArea->GetInstanceByID(NewSenderID); + CScriptObject *pOldSender = mpArea->InstanceByID(OldSenderID); + CScriptObject *pNewSender = mpArea->InstanceByID(NewSenderID); mSenderID = NewSenderID; pOldSender->RemoveLink(eOutgoing, this); @@ -63,8 +63,8 @@ public: void SetReceiver(u32 NewReceiverID, u32 Index = -1) { u32 OldReceiverID = mSenderID; - CScriptObject *pOldReceiver = mpArea->GetInstanceByID(OldReceiverID); - CScriptObject *pNewReceiver = mpArea->GetInstanceByID(NewReceiverID); + CScriptObject *pOldReceiver = mpArea->InstanceByID(OldReceiverID); + CScriptObject *pNewReceiver = mpArea->InstanceByID(NewReceiverID); mReceiverID = NewReceiverID; pOldReceiver->RemoveLink(eIncoming, this); @@ -73,7 +73,7 @@ public: u32 SenderIndex() const { - CScriptObject *pSender = mpArea->GetInstanceByID(mSenderID); + CScriptObject *pSender = mpArea->InstanceByID(mSenderID); for (u32 iLink = 0; iLink < pSender->NumLinks(eOutgoing); iLink++) { @@ -86,7 +86,7 @@ public: u32 ReceiverIndex() const { - CScriptObject *pReceiver = mpArea->GetInstanceByID(mReceiverID); + CScriptObject *pReceiver = mpArea->InstanceByID(mReceiverID); for (u32 iLink = 0; iLink < pReceiver->NumLinks(eIncoming); iLink++) { @@ -117,8 +117,8 @@ public: u32 Message() const { return mMessageID; } u32 SenderID() const { return mSenderID; } u32 ReceiverID() const { return mReceiverID; } - CScriptObject* Sender() const { return mpArea->GetInstanceByID(mSenderID); } - CScriptObject* Receiver() const { return mpArea->GetInstanceByID(mReceiverID); } + CScriptObject* Sender() const { return mpArea->InstanceByID(mSenderID); } + CScriptObject* Receiver() const { return mpArea->InstanceByID(mReceiverID); } void SetState(u32 StateID) { mStateID = StateID; } void SetMessage(u32 MessageID) { mMessageID = MessageID; } diff --git a/src/Core/Resource/Script/CScriptLayer.h b/src/Core/Resource/Script/CScriptLayer.h index 23079481..4bd8ae35 100644 --- a/src/Core/Resource/Script/CScriptLayer.h +++ b/src/Core/Resource/Script/CScriptLayer.h @@ -8,13 +8,15 @@ class CScriptLayer { + CGameArea *mpArea; TString mLayerName; bool mActive; bool mVisible; std::vector mInstances; public: - CScriptLayer() - : mLayerName("New Layer") + CScriptLayer(CGameArea *pArea) + : mpArea(pArea) + , mLayerName("New Layer") , mActive(true) , mVisible(true) { @@ -27,9 +29,17 @@ public: } // Data Manipulation - void AddInstance(CScriptObject *pObject) + void AddInstance(CScriptObject *pObject, u32 Index = -1) { - mInstances.push_back(pObject); + if (Index != -1 && Index < mInstances.size()) + { + auto it = mInstances.begin(); + std::advance(it, Index); + mInstances.insert(it, pObject); + } + + else + mInstances.push_back(pObject); } void RemoveInstance(CScriptObject *pInstance) @@ -67,6 +77,7 @@ public: } // Accessors + inline CGameArea* Area() const { return mpArea; } inline TString Name() const { return mLayerName; } inline bool IsActive() const { return mActive; } inline bool IsVisible() const { return mVisible; } @@ -88,6 +99,17 @@ public: inline void SetActive(bool Active) { mActive = Active; } 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 CScriptObject* operator[](u32 Index) { return InstanceByIndex(Index); } }; diff --git a/src/Core/Resource/Script/CScriptObject.cpp b/src/Core/Resource/Script/CScriptObject.cpp index a0990e6c..1ed8538e 100644 --- a/src/Core/Resource/Script/CScriptObject.cpp +++ b/src/Core/Resource/Script/CScriptObject.cpp @@ -3,11 +3,12 @@ #include "CMasterTemplate.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) , mpArea(pArea) , mpLayer(pLayer) , mVersion(0) + , mInstanceID(InstanceID) , mpDisplayModel(nullptr) , mpCollision(nullptr) , 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) { mpLayer->RemoveInstance(this); 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 { /* 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 { if (mpInstanceName) diff --git a/src/Core/Resource/Script/CScriptObject.h b/src/Core/Resource/Script/CScriptObject.h index df08c38c..91733b7a 100644 --- a/src/Core/Resource/Script/CScriptObject.h +++ b/src/Core/Resource/Script/CScriptObject.h @@ -50,7 +50,7 @@ class CScriptObject mutable bool mIsCheckingNearVisibleActivation; public: - CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate); + CScriptObject(u32 InstanceID, CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate); ~CScriptObject(); void EvaluateProperties(); @@ -59,7 +59,8 @@ public: void EvaluateCollisionModel(); void EvaluateVolume(); bool IsEditorProperty(IProperty *pProp); - void SetLayer(CScriptLayer *pLayer); + void SetLayer(CScriptLayer *pLayer, u32 NewLayerIndex = -1); + u32 LayerIndex() const; bool HasNearVisibleActivation() const; CScriptTemplate* Template() const; @@ -78,6 +79,7 @@ public: CLink* Link(ELinkType Type, u32 Index) const; void AddLink(ELinkType Type, CLink *pLink, u32 Index = -1); void RemoveLink(ELinkType Type, CLink *pLink); + void BreakAllLinks(); CVector3f Position() const; CVector3f Rotation() const; diff --git a/src/Core/Scene/CScene.cpp b/src/Core/Scene/CScene.cpp index 0c53c966..45c16089 100644 --- a/src/Core/Scene/CScene.cpp +++ b/src/Core/Scene/CScene.cpp @@ -65,6 +65,18 @@ CScriptNode* CScene::CreateScriptNode(CScriptObject *pObj) CScriptNode *pNode = new CScriptNode(this, mpAreaRootNode, pObj); 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++; return pNode; } @@ -79,6 +91,48 @@ CLightNode* CScene::CreateLightNode(CLight *pLight) 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(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) { // Clear existing area @@ -121,20 +175,7 @@ void CScene::SetActiveArea(CGameArea *pArea) for (u32 iObj = 0; iObj < NumObjects; iObj++) { CScriptObject *pObj = pLayer->InstanceByIndex(iObj); - CScriptNode *pNode = 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; - } + CreateScriptNode(pObj); } } @@ -144,10 +185,7 @@ void CScene::SetActiveArea(CGameArea *pArea) for (u32 iObj = 0; iObj < pGenLayer->NumInstances(); iObj++) { CScriptObject *pObj = pGenLayer->InstanceByIndex(iObj); - CScriptNode *pNode = CreateScriptNode(pObj); - - // Add to map - mScriptNodeMap[pObj->InstanceID()] = pNode; + CreateScriptNode(pObj); } } @@ -217,16 +255,10 @@ void CScene::AddSceneToRenderer(CRenderer *pRenderer, const SViewInfo& ViewInfo) FShowFlags ShowFlags = (ViewInfo.GameMode ? gkGameModeShowFlags : ViewInfo.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) - { - std::vector& rNodeVec = it->second; - - for (u32 iNode = 0; iNode < rNodeVec.size(); iNode++) - if (ViewInfo.GameMode || rNodeVec[iNode]->IsVisible()) - rNodeVec[iNode]->AddToRenderer(pRenderer, ViewInfo); - } + if (ViewInfo.GameMode || It->IsVisible()) + It->AddToRenderer(pRenderer, ViewInfo); } } @@ -236,16 +268,10 @@ SRayIntersection CScene::SceneRayCast(const CRay& Ray, const SViewInfo& ViewInfo FNodeFlags NodeFlags = NodeFlagsForShowFlags(ShowFlags); CRayCollisionTester Tester(Ray); - for (auto it = mNodes.begin(); it != mNodes.end(); it++) + for (CSceneIterator It(this, NodeFlags, false); It; ++It) { - if (NodeFlags & it->first) - { - std::vector& rNodeVec = it->second; - - for (u32 iNode = 0; iNode < rNodeVec.size(); iNode++) - if (rNodeVec[iNode]->IsVisible()) - rNodeVec[iNode]->RayAABoxIntersectTest(Tester, ViewInfo); - } + if (It->IsVisible()) + It->RayAABoxIntersectTest(Tester, ViewInfo); } return Tester.TestNodes(ViewInfo); diff --git a/src/Core/Scene/CScene.h b/src/Core/Scene/CScene.h index 8f2fa421..3216d7f2 100644 --- a/src/Core/Scene/CScene.h +++ b/src/Core/Scene/CScene.h @@ -51,6 +51,7 @@ public: CCollisionNode* CreateCollisionNode(CCollisionMeshGroup *pMesh); CScriptNode* CreateScriptNode(CScriptObject *pObj); CLightNode* CreateLightNode(CLight *pLight); + void DeleteNode(CSceneNode *pNode); void SetActiveArea(CGameArea *pArea); void SetActiveWorld(CWorld *pWorld); void PostLoad(); diff --git a/src/Core/Scene/CSceneIterator.h b/src/Core/Scene/CSceneIterator.h index 900a609b..ebb819b4 100644 --- a/src/Core/Scene/CSceneIterator.h +++ b/src/Core/Scene/CSceneIterator.h @@ -38,7 +38,7 @@ public: inline operator bool() const { - return DoneIterating(); + return !DoneIterating(); } inline CSceneNode* operator*() const diff --git a/src/Editor/CNodeSelection.cpp b/src/Editor/CNodeSelection.cpp deleted file mode 100644 index 7caf4050..00000000 --- a/src/Editor/CNodeSelection.cpp +++ /dev/null @@ -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); -} diff --git a/src/Editor/CNodeSelection.h b/src/Editor/CNodeSelection.h index 6ff0a231..7734e4e5 100644 --- a/src/Editor/CNodeSelection.h +++ b/src/Editor/CNodeSelection.h @@ -1,24 +1,126 @@ #ifndef CNODESELECTION_H #define CNODESELECTION_H +#include #include -#include +#include +#include +#include -class CSceneSelection +class CNodeSelection : public QObject { - CScene *mpScene; - std::vector mSelectedNodes; + Q_OBJECT + + FNodeFlags mAllowedNodes; + QList mSelectedNodes; + + mutable CAABox mCachedBounds; + mutable bool mBoundsDirty; public: - CSceneSelection(CScene *pScene); - void SelectNode(CSceneNode *pNode); - void DeselectNode(CSceneNode *pNode); - u32 SelectionSize(); - CSceneNode* NodeByIndex(u32 Index); - void ClearSelection(); + CNodeSelection() + : mAllowedNodes(eAllNodeTypes) + , mBoundsDirty(true) {} - // Operators - CSceneNode* operator[](u32 Index); + ~CNodeSelection() + { + 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& 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(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 SelectedNodeList() const { return mSelectedNodes; } + +signals: + void Modified(); }; #endif // CNODESELECTION_H diff --git a/src/Editor/CSceneViewport.cpp b/src/Editor/CSceneViewport.cpp index 145e7c6a..8aac6d01 100644 --- a/src/Editor/CSceneViewport.cpp +++ b/src/Editor/CSceneViewport.cpp @@ -1,4 +1,5 @@ #include "CSceneViewport.h" +#include "CSelectionIterator.h" #include "UICommon.h" #include "Editor/Undo/UndoCommands.h" #include @@ -386,10 +387,8 @@ void CSceneViewport::OnToggleSelect() void CSceneViewport::OnHideSelection() { - const QList& rkSelection = mpEditor->GetSelection(); - - foreach (CSceneNode *pNode, rkSelection) - pNode->SetVisible(false); + for (CSelectionIterator It(mpEditor->Selection()); It; ++It) + It->SetVisible(false); } void CSceneViewport::OnHideUnselected() diff --git a/src/Editor/CSelectionIterator.h b/src/Editor/CSelectionIterator.h new file mode 100644 index 00000000..b9e1bb13 --- /dev/null +++ b/src/Editor/CSelectionIterator.h @@ -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 + diff --git a/src/Editor/CStartWindow.cpp b/src/Editor/CStartWindow.cpp index d863e569..a44f148c 100644 --- a/src/Editor/CStartWindow.cpp +++ b/src/Editor/CStartWindow.cpp @@ -183,8 +183,9 @@ void CStartWindow::on_LaunchWorldEditorButton_clicked() else { - mpWorld->SetAreaLayerInfo(pArea, mSelectedAreaIndex); - mpWorldEditor->SetArea(mpWorld, pArea, mSelectedAreaIndex); + pArea->SetWorldIndex(mSelectedAreaIndex); + mpWorld->SetAreaLayerInfo(pArea); + mpWorldEditor->SetArea(mpWorld, pArea); gResCache.Clean(); mpWorldEditor->setWindowModality(Qt::WindowModal); diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index d3159ec5..6253a4ec 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -149,17 +149,19 @@ HEADERS += \ Undo/CAddLinkCommand.h \ Undo/CDeleteLinksCommand.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 SOURCES += \ ModelEditor/CModelEditorViewport.cpp \ ModelEditor/CModelEditorWindow.cpp \ - Undo/CClearSelectionCommand.cpp \ - Undo/CDeselectNodeCommand.cpp \ Undo/CRotateNodeCommand.cpp \ Undo/CScaleNodeCommand.cpp \ - Undo/CSelectNodeCommand.cpp \ Undo/CTranslateNodeCommand.cpp \ Widgets/IPreviewPanel.cpp \ Widgets/WAnimParamsEditor.cpp \ @@ -185,7 +187,6 @@ SOURCES += \ CBasicViewport.cpp \ CDarkStyle.cpp \ CGizmo.cpp \ - CNodeSelection.cpp \ CSceneViewport.cpp \ CStartWindow.cpp \ INodeEditor.cpp \ @@ -193,8 +194,6 @@ SOURCES += \ TestDialog.cpp \ UICommon.cpp \ CErrorLogDialog.cpp \ - Undo/CSelectAllCommand.cpp \ - Undo/CInvertSelectionCommand.cpp \ WorldEditor/CPoiMapEditDialog.cpp \ WorldEditor/CPoiMapModel.cpp \ PropertyEdit/CPropertyModel.cpp \ @@ -211,7 +210,9 @@ SOURCES += \ WorldEditor/CSelectInstanceDialog.cpp \ Undo/CAddLinkCommand.cpp \ Undo/CDeleteLinksCommand.cpp \ - Undo/CEditLinkCommand.cpp + Undo/CEditLinkCommand.cpp \ + Undo/CDeleteSelectionCommand.cpp \ + Undo/CCreateInstanceCommand.cpp # UI Files FORMS += \ diff --git a/src/Editor/INodeEditor.cpp b/src/Editor/INodeEditor.cpp index 7ce4ef6d..7b346b83 100644 --- a/src/Editor/INodeEditor.cpp +++ b/src/Editor/INodeEditor.cpp @@ -1,11 +1,12 @@ #include "INodeEditor.h" +#include "CSelectionIterator.h" #include "Editor/Undo/UndoCommands.h" #include INodeEditor::INodeEditor(QWidget *pParent) : QMainWindow(pParent) , mPickMode(false) - , mSelectionNodeFlags(eAllNodeTypes) + , mpSelection(new CNodeSelection) , mSelectionLocked(false) , mShowGizmo(false) , mGizmoHovering(false) @@ -53,10 +54,12 @@ INodeEditor::INodeEditor(QWidget *pParent) connect(mGizmoActions[2], SIGNAL(triggered()), this, SLOT(OnRotateTriggered())); connect(mGizmoActions[3], SIGNAL(triggered()), this, SLOT(OnScaleTriggered())); connect(mpTransformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnTransformSpaceChanged(int))); + connect(mpSelection, SIGNAL(Modified()), this, SLOT(OnSelectionModified())); } INodeEditor::~INodeEditor() { + delete mpSelection; } QUndoStack* INodeEditor::UndoStack() @@ -76,7 +79,7 @@ CGizmo* INodeEditor::Gizmo() bool INodeEditor::IsGizmoVisible() { - return (mShowGizmo && !mSelection.empty()); + return (mShowGizmo && !mpSelection->IsEmpty()); } 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(pNode); - - if (pScript->HasPreviewVolume()) - mSelectionBounds.ExpandBounds(pScript->PreviewVolumeAABox()); - } -} - void INodeEditor::SelectNode(CSceneNode *pNode) { if (!mSelectionLocked) { 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 (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 (!mSelection.empty()) - mUndoStack.push(new CClearSelectionCommand(this, mSelection)); + if (!mpSelection->IsEmpty()) + mUndoStack.push(new CClearSelectionCommand(mpSelection)); } } @@ -165,17 +147,17 @@ void INodeEditor::ClearAndSelectNode(CSceneNode *pNode) { if (!mSelectionLocked) { - if (mSelection.empty()) - mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); + if (mpSelection->IsEmpty()) + mUndoStack.push(new CSelectNodeCommand(mpSelection, pNode)); - else if ((mSelection.size() == 1) && (mSelection.front() == pNode)) + else if ((mpSelection->Size() == 1) && (mpSelection->Front() == pNode)) return; else { mUndoStack.beginMacro("Select"); - mUndoStack.push(new CClearSelectionCommand(this, mSelection)); - mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); + mUndoStack.push(new CClearSelectionCommand(mpSelection)); + mUndoStack.push(new CSelectNodeCommand(mpSelection, pNode)); mUndoStack.endMacro(); } } @@ -184,13 +166,13 @@ void INodeEditor::ClearAndSelectNode(CSceneNode *pNode) void INodeEditor::SelectAll(FNodeFlags NodeFlags) { if (!mSelectionLocked) - mUndoStack.push(new CSelectAllCommand(this, mSelection, &mScene, NodeFlags)); + mUndoStack.push(new CSelectAllCommand(mpSelection, &mScene, NodeFlags)); } void INodeEditor::InvertSelection(FNodeFlags NodeFlags) { if (!mSelectionLocked) - mUndoStack.push(new CInvertSelectionCommand(this, mSelection, &mScene, NodeFlags)); + mUndoStack.push(new CInvertSelectionCommand(mpSelection, &mScene, NodeFlags)); } void INodeEditor::SetSelectionLocked(bool Locked) @@ -200,12 +182,12 @@ void INodeEditor::SetSelectionLocked(bool Locked) bool INodeEditor::HasSelection() const { - return (!mSelection.isEmpty()); + return (!mpSelection->IsEmpty()); } -const QList& 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*/) @@ -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(); emit SelectionModified(); } -void INodeEditor::NotifySelectionTransformed() -{ - foreach (CSceneNode *pNode, mSelection) - pNode->OnTransformed(); - - emit SelectionTransformed(); -} - -// ************ PUBLIC SLOTS ************ void INodeEditor::OnGizmoMoved() { switch (mGizmo.Mode()) @@ -254,26 +256,25 @@ void INodeEditor::OnGizmoMoved() case CGizmo::eTranslate: { CVector3f delta = mGizmo.DeltaTranslation(); - mUndoStack.push(new CTranslateNodeCommand(this, mSelection, delta, mTranslateSpace)); + mUndoStack.push(new CTranslateNodeCommand(this, mpSelection->SelectedNodeList(), delta, mTranslateSpace)); break; } case CGizmo::eRotate: { 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; } case CGizmo::eScale: { CVector3f delta = mGizmo.DeltaScale(); - mUndoStack.push(new CScaleNodeCommand(this, mSelection, CVector3f::skZero, delta)); + mUndoStack.push(new CScaleNodeCommand(this, mpSelection->SelectedNodeList(), CVector3f::skZero, delta)); break; } } - RecalculateSelectionBounds(); UpdateGizmoUI(); } @@ -285,7 +286,7 @@ void INodeEditor::OnViewportClick(const SRayIntersection& rkRayIntersect, QMouse // Not in pick mode: process node selection/deselection if (!mPickMode) { - bool ValidNode = (pNode && (pNode->NodeType() & mSelectionNodeFlags)); + bool ValidNode = (pNode && mpSelection->IsAllowedType(pNode)); bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0); bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0); @@ -364,11 +365,11 @@ void INodeEditor::UpdateTransformActionsEnabled() bool AllowTranslate = true, AllowRotate = true, AllowScale = true; 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)->AllowsRotate()) AllowRotate = false; - if (!(*it)->AllowsScale()) AllowScale = false; + if (!It->AllowsTranslate()) AllowTranslate = false; + if (!It->AllowsRotate()) AllowRotate = false; + if (!It->AllowsScale()) AllowScale = false; } mGizmoActions[1]->setEnabled(AllowTranslate); diff --git a/src/Editor/INodeEditor.h b/src/Editor/INodeEditor.h index 99193f08..eef511f6 100644 --- a/src/Editor/INodeEditor.h +++ b/src/Editor/INodeEditor.h @@ -2,6 +2,7 @@ #define INODEEDITOR_H #include "CGizmo.h" +#include "CNodeSelection.h" #include #include @@ -23,9 +24,7 @@ protected: // Node management CScene mScene; - QList mSelection; - FNodeFlags mSelectionNodeFlags; - CAABox mSelectionBounds; + CNodeSelection *mpSelection; bool mSelectionLocked; // Gizmo @@ -62,8 +61,6 @@ public: void EndGizmoTransform(); ETransformSpace CurrentTransformSpace(); - void RecalculateSelectionBounds(); - void ExpandSelectionBounds(CSceneNode *pNode); void SelectNode(CSceneNode *pNode); void DeselectNode(CSceneNode *pNode); void ClearSelection(); @@ -72,15 +69,22 @@ public: void InvertSelection(FNodeFlags NodeFlags); void SetSelectionLocked(bool Locked); bool HasSelection() const; - const QList& GetSelection() const; + CNodeSelection* Selection() const; void EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor = Qt::CrossCursor); void ExitPickMode(); - void NotifySelectionModified(); void NotifySelectionTransformed(); + virtual void NotifyNodeAboutToBeSpawned(); + virtual void NotifyNodeSpawned(CSceneNode *pNode); + virtual void NotifyNodeAboutToBeDeleted(CSceneNode *pNode); + virtual void NotifyNodeDeleted(); signals: + void NodeAboutToBeSpawned(); + void NodeSpawned(CSceneNode *pNode); + void NodeAboutToBeDeleted(CSceneNode *pNode); + void NodeDeleted(); void SelectionModified(); void SelectionTransformed(); @@ -90,6 +94,7 @@ signals: void PickModeHoverChanged(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent); public slots: + void OnSelectionModified(); void OnGizmoMoved(); virtual void UpdateGizmoUI() = 0; virtual void UpdateSelectionUI() = 0; diff --git a/src/Editor/PropertyEdit/CPropertyModel.cpp b/src/Editor/PropertyEdit/CPropertyModel.cpp index 1917fc93..99edac37 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.cpp +++ b/src/Editor/PropertyEdit/CPropertyModel.cpp @@ -499,7 +499,7 @@ Qt::ItemFlags CPropertyModel::flags(const QModelIndex& rkIndex) const else return (Qt::ItemIsEnabled | Qt::ItemIsEditable); } -void CPropertyModel::NotifyPropertyModified(IProperty *pProp) +void CPropertyModel::NotifyPropertyModified(class CScriptObject*, IProperty *pProp) { NotifyPropertyModified(IndexForProperty(pProp)); } diff --git a/src/Editor/PropertyEdit/CPropertyModel.h b/src/Editor/PropertyEdit/CPropertyModel.h index 113ba6a0..d05f3071 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.h +++ b/src/Editor/PropertyEdit/CPropertyModel.h @@ -35,7 +35,7 @@ public: inline void SetBoldModifiedProperties(bool Enable) { mBoldModifiedProperties = Enable; } public slots: - void NotifyPropertyModified(IProperty *pProp); + void NotifyPropertyModified(class CScriptObject *pInst, IProperty *pProp); void NotifyPropertyModified(const QModelIndex& rkIndex); signals: diff --git a/src/Editor/PropertyEdit/CPropertyView.cpp b/src/Editor/PropertyEdit/CPropertyView.cpp index 2400f3f5..1d220bd1 100644 --- a/src/Editor/PropertyEdit/CPropertyView.cpp +++ b/src/Editor/PropertyEdit/CPropertyView.cpp @@ -75,7 +75,7 @@ void CPropertyView::SetEditor(CWorldEditor *pEditor) { mpEditor = 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) diff --git a/src/Editor/Undo/CClearSelectionCommand.cpp b/src/Editor/Undo/CClearSelectionCommand.cpp deleted file mode 100644 index 794a43c8..00000000 --- a/src/Editor/Undo/CClearSelectionCommand.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "CClearSelectionCommand.h" -#include "Editor/INodeEditor.h" - -CClearSelectionCommand::CClearSelectionCommand(INodeEditor *pEditor, QList& 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(); -} diff --git a/src/Editor/Undo/CClearSelectionCommand.h b/src/Editor/Undo/CClearSelectionCommand.h index 2bc8c642..cfa2a6fa 100644 --- a/src/Editor/Undo/CClearSelectionCommand.h +++ b/src/Editor/Undo/CClearSelectionCommand.h @@ -7,14 +7,18 @@ class CClearSelectionCommand : public IUndoCommand { - INodeEditor *mpEditor; - QList mSelectionState; - QList *mpSelection; + QList mOldSelection; + CNodeSelection *mpSelection; + public: - CClearSelectionCommand(INodeEditor *pEditor, QList& selection); - ~CClearSelectionCommand(); - void undo(); - void redo(); + CClearSelectionCommand(CNodeSelection *pSelection) + : IUndoCommand("Clear Selection"), + mOldSelection(pSelection->SelectedNodeList()), + mpSelection(pSelection) + {} + + void undo() { mpSelection->SetSelectedNodes(mOldSelection); } + void redo() { mpSelection->Clear(); } bool AffectsCleanState() const { return false; } }; diff --git a/src/Editor/Undo/CCreateInstanceCommand.cpp b/src/Editor/Undo/CCreateInstanceCommand.cpp new file mode 100644 index 00000000..18f3cf5b --- /dev/null +++ b/src/Editor/Undo/CCreateInstanceCommand.cpp @@ -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); +} diff --git a/src/Editor/Undo/CCreateInstanceCommand.h b/src/Editor/Undo/CCreateInstanceCommand.h new file mode 100644 index 00000000..a03714fe --- /dev/null +++ b/src/Editor/Undo/CCreateInstanceCommand.h @@ -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 +#include +#include + +class CCreateInstanceCommand : public IUndoCommand +{ + CWorldEditor *mpEditor; + CScene *mpScene; + CGameArea *mpArea; + CScriptTemplate *mpTemplate; + u32 mLayerIndex; + CVector3f mSpawnPosition; + + QList 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 diff --git a/src/Editor/Undo/CDeleteSelectionCommand.cpp b/src/Editor/Undo/CDeleteSelectionCommand.cpp new file mode 100644 index 00000000..83405975 --- /dev/null +++ b/src/Editor/Undo/CDeleteSelectionCommand.cpp @@ -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(pNode)->Object(); + mpEditor->NotifyNodeAboutToBeDeleted(pNode); + mpEditor->Scene()->DeleteNode(pNode); + mpEditor->ActiveArea()->DeleteInstance(pInst); + mpEditor->NotifyNodeDeleted(); + } +} diff --git a/src/Editor/Undo/CDeleteSelectionCommand.h b/src/Editor/Undo/CDeleteSelectionCommand.h new file mode 100644 index 00000000..04c4f470 --- /dev/null +++ b/src/Editor/Undo/CDeleteSelectionCommand.h @@ -0,0 +1,22 @@ +#ifndef CDELETESELECTIONCOMMAND_H +#define CDELETESELECTIONCOMMAND_H + +#include "IUndoCommand.h" +#include "Editor/WorldEditor/CWorldEditor.h" +#include + +class CDeleteSelectionCommand : public IUndoCommand +{ + CWorldEditor *mpEditor; + QList mNodesToDelete; + QList mOldSelection; + QList mNewSelection; + +public: + CDeleteSelectionCommand(CWorldEditor *pEditor); + void undo(); + void redo(); + bool AffectsCleanState() const { return true; } +}; + +#endif // CDELETESELECTIONCOMMAND_H diff --git a/src/Editor/Undo/CDeselectNodeCommand.cpp b/src/Editor/Undo/CDeselectNodeCommand.cpp deleted file mode 100644 index 545184ba..00000000 --- a/src/Editor/Undo/CDeselectNodeCommand.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "CDeselectNodeCommand.h" -#include "Editor/INodeEditor.h" - -CDeselectNodeCommand::CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& 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(); -} diff --git a/src/Editor/Undo/CDeselectNodeCommand.h b/src/Editor/Undo/CDeselectNodeCommand.h index f8035cc2..ccb96715 100644 --- a/src/Editor/Undo/CDeselectNodeCommand.h +++ b/src/Editor/Undo/CDeselectNodeCommand.h @@ -7,13 +7,17 @@ class CDeselectNodeCommand : public IUndoCommand { - INodeEditor *mpEditor; CSceneNode *mpNode; - QList *mpSelection; + CNodeSelection *mpSelection; public: - CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection); - void undo(); - void redo(); + CDeselectNodeCommand(CNodeSelection *pSelection, CSceneNode *pNode) + : IUndoCommand("Deselect") + , mpNode(pNode) + , mpSelection(pSelection) + {} + + void undo() { mpSelection->SelectNode(mpNode); } + void redo() { mpSelection->DeselectNode(mpNode); } bool AffectsCleanState() const { return false; } }; diff --git a/src/Editor/Undo/CInvertSelectionCommand.cpp b/src/Editor/Undo/CInvertSelectionCommand.cpp deleted file mode 100644 index a85202ae..00000000 --- a/src/Editor/Undo/CInvertSelectionCommand.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "CInvertSelectionCommand.h" -#include - -CInvertSelectionCommand::CInvertSelectionCommand(INodeEditor *pEditor, QList& 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(); -} diff --git a/src/Editor/Undo/CInvertSelectionCommand.h b/src/Editor/Undo/CInvertSelectionCommand.h index d010d7ee..0723105b 100644 --- a/src/Editor/Undo/CInvertSelectionCommand.h +++ b/src/Editor/Undo/CInvertSelectionCommand.h @@ -7,17 +7,27 @@ class CInvertSelectionCommand : public IUndoCommand { - INodeEditor *mpEditor; + CNodeSelection *mpSelection; QList mOldSelection; QList mNewSelection; - QList *mpSelection; public: - CInvertSelectionCommand(INodeEditor *pEditor, QList& rSelection, CScene *pScene, FNodeFlags NodeFlags); - ~CInvertSelectionCommand(); - void undo(); - void redo(); - virtual bool AffectsCleanState() const { return false; } + CInvertSelectionCommand(CNodeSelection *pSelection, CScene *pScene, FNodeFlags NodeFlags) + : IUndoCommand("Invert Selection") + , mpSelection(pSelection) + { + 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 diff --git a/src/Editor/Undo/CRotateNodeCommand.cpp b/src/Editor/Undo/CRotateNodeCommand.cpp index 7da64cfb..d52fb51f 100644 --- a/src/Editor/Undo/CRotateNodeCommand.cpp +++ b/src/Editor/Undo/CRotateNodeCommand.cpp @@ -79,7 +79,6 @@ void CRotateNodeCommand::undo() rotate.pNode->SetRotation(rotate.initialRot); } - mpEditor->RecalculateSelectionBounds(); mpEditor->NotifySelectionTransformed(); mpEditor->UpdateGizmoUI(); } @@ -94,7 +93,6 @@ void CRotateNodeCommand::redo() rotate.pNode->SetRotation(rotate.newRot); } - mpEditor->RecalculateSelectionBounds(); mpEditor->NotifySelectionTransformed(); mpEditor->UpdateGizmoUI(); } diff --git a/src/Editor/Undo/CScaleNodeCommand.cpp b/src/Editor/Undo/CScaleNodeCommand.cpp index 23b86cfc..f7921725 100644 --- a/src/Editor/Undo/CScaleNodeCommand.cpp +++ b/src/Editor/Undo/CScaleNodeCommand.cpp @@ -79,7 +79,6 @@ void CScaleNodeCommand::undo() scale.pNode->SetScale(scale.initialScale); } - mpEditor->RecalculateSelectionBounds(); mpEditor->NotifySelectionTransformed(); mpEditor->UpdateGizmoUI(); } @@ -94,7 +93,6 @@ void CScaleNodeCommand::redo() scale.pNode->SetScale(scale.newScale); } - mpEditor->RecalculateSelectionBounds(); mpEditor->NotifySelectionTransformed(); mpEditor->UpdateGizmoUI(); } diff --git a/src/Editor/Undo/CSelectAllCommand.cpp b/src/Editor/Undo/CSelectAllCommand.cpp deleted file mode 100644 index a3e057c4..00000000 --- a/src/Editor/Undo/CSelectAllCommand.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "CSelectAllCommand.h" -#include - -CSelectAllCommand::CSelectAllCommand(INodeEditor *pEditor, QList &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(); -} diff --git a/src/Editor/Undo/CSelectAllCommand.h b/src/Editor/Undo/CSelectAllCommand.h index 3f5844a8..b4b5e929 100644 --- a/src/Editor/Undo/CSelectAllCommand.h +++ b/src/Editor/Undo/CSelectAllCommand.h @@ -7,16 +7,22 @@ class CSelectAllCommand : public IUndoCommand { - INodeEditor *mpEditor; QList mOldSelection; QList mNewSelection; - QList *mpSelection; + CNodeSelection *mpSelection; public: - CSelectAllCommand(INodeEditor *pEditor, QList& rSelection, CScene *pScene, FNodeFlags NodeFlags); - ~CSelectAllCommand(); - void undo(); - void redo(); + CSelectAllCommand(CNodeSelection *pSelection, CScene *pScene, FNodeFlags NodeFlags) + : IUndoCommand("Select All") + , mOldSelection(pSelection->SelectedNodeList()) + , 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; } }; diff --git a/src/Editor/Undo/CSelectNodeCommand.cpp b/src/Editor/Undo/CSelectNodeCommand.cpp deleted file mode 100644 index 7813b2dc..00000000 --- a/src/Editor/Undo/CSelectNodeCommand.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "CSelectNodeCommand.h" -#include "Editor/INodeEditor.h" - -CSelectNodeCommand::CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& 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(); -} diff --git a/src/Editor/Undo/CSelectNodeCommand.h b/src/Editor/Undo/CSelectNodeCommand.h index eb27f20f..cc8e9c3c 100644 --- a/src/Editor/Undo/CSelectNodeCommand.h +++ b/src/Editor/Undo/CSelectNodeCommand.h @@ -7,13 +7,18 @@ class CSelectNodeCommand : public IUndoCommand { - INodeEditor *mpEditor; CSceneNode *mpNode; - QList *mpSelection; + CNodeSelection *mpSelection; + public: - CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection); - void undo(); - void redo(); + CSelectNodeCommand(CNodeSelection *pSelection, CSceneNode *pNode) + : IUndoCommand("Select") + , mpNode(pNode) + , mpSelection(pSelection) + {} + + void undo() { mpSelection->DeselectNode(mpNode); } + void redo() { mpSelection->SelectNode(mpNode); } bool AffectsCleanState() const { return false; } }; diff --git a/src/Editor/Undo/CTranslateNodeCommand.cpp b/src/Editor/Undo/CTranslateNodeCommand.cpp index 734a24dd..e0be7eb8 100644 --- a/src/Editor/Undo/CTranslateNodeCommand.cpp +++ b/src/Editor/Undo/CTranslateNodeCommand.cpp @@ -71,7 +71,6 @@ void CTranslateNodeCommand::undo() foreach (SNodeTranslate translate, mNodeList) translate.pNode->SetPosition(translate.initialPos); - mpEditor->RecalculateSelectionBounds(); mpEditor->NotifySelectionTransformed(); mpEditor->UpdateGizmoUI(); } @@ -83,7 +82,6 @@ void CTranslateNodeCommand::redo() foreach (SNodeTranslate translate, mNodeList) translate.pNode->SetPosition(translate.newPos); - mpEditor->RecalculateSelectionBounds(); mpEditor->NotifySelectionTransformed(); mpEditor->UpdateGizmoUI(); } diff --git a/src/Editor/Undo/UndoCommands.h b/src/Editor/Undo/UndoCommands.h index a1066b5a..27bfbfc9 100644 --- a/src/Editor/Undo/UndoCommands.h +++ b/src/Editor/Undo/UndoCommands.h @@ -1,6 +1,8 @@ #ifndef UNDOCOMMANDS #define UNDOCOMMANDS +#include "CCreateInstanceCommand.h" + #include "CTranslateNodeCommand.h" #include "CRotateNodeCommand.h" #include "CScaleNodeCommand.h" @@ -10,6 +12,7 @@ #include "CClearSelectionCommand.h" #include "CSelectAllCommand.h" #include "CInvertSelectionCommand.h" +#include "CDeleteSelectionCommand.h" #include "CEditScriptPropertyCommand.h" #include "CResizeScriptArrayCommand.h" diff --git a/src/Editor/WorldEditor/CInstancesModel.cpp b/src/Editor/WorldEditor/CInstancesModel.cpp index 127ac200..62c0284e 100644 --- a/src/Editor/WorldEditor/CInstancesModel.cpp +++ b/src/Editor/WorldEditor/CInstancesModel.cpp @@ -26,11 +26,6 @@ #define TYPES_NODE_TYPE_SHIFT 1 #define TYPES_ITEM_TYPE_SHIFT 0 -bool SortTemplatesAlphabetical(CScriptTemplate *pA, CScriptTemplate *pB) -{ - return (pA->Name() < pB->Name()); -} - CInstancesModel::CInstancesModel(QObject *pParent) : QAbstractItemModel(pParent) { mpEditor = nullptr; @@ -40,6 +35,7 @@ CInstancesModel::CInstancesModel(QObject *pParent) : QAbstractItemModel(pParent) mModelType = eLayers; mShowColumnEnabled = true; mBaseItems << "Script"; + mAddingOrRemovingRows = false; } CInstancesModel::~CInstancesModel() @@ -319,6 +315,11 @@ void CInstancesModel::SetEditor(CWorldEditor *pEditor) mpEditor = pEditor; 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(InstancesLayerChanged(QList)), this, SLOT(InstancesLayerPostChange(QList))); } @@ -347,52 +348,6 @@ void CInstancesModel::SetShowColumnEnabled(bool Enabled) emit layoutChanged(); } -void CInstancesModel::NodeCreated(CSceneNode *pNode) -{ - if (mModelType == eTypes) - { - if (pNode->NodeType() == eScriptNode) - { - CScriptNode *pScript = static_cast(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(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 { if ((mModelType != eLayers) || (IndexNodeType(index) != eScriptType) || (IndexType(index) != eObjectTypeIndex)) @@ -420,6 +375,107 @@ CScriptObject* CInstancesModel::IndexObject(const QModelIndex& index) const } // ************ 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(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(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 InstList = QList::fromStdList(pInst->Template()->ObjectList()); + u32 InstIdx = InstList.indexOf(pInst); + QModelIndex InstIndex = index(InstIdx, 0, TempIndex); + emit dataChanged(InstIndex, InstIndex); + } + } +} + void CInstancesModel::InstancesLayerPreChange() { // This is only really needed on layers, which have rows moved. @@ -501,7 +557,9 @@ void CInstancesModel::GenerateList() mTemplateList << pTemp; } - qSort(mTemplateList.begin(), mTemplateList.end(), SortTemplatesAlphabetical); + qSort(mTemplateList.begin(), mTemplateList.end(), [](CScriptTemplate *pLeft, CScriptTemplate *pRight) -> bool { + return (pLeft->Name() < pRight->Name()); + }); } endResetModel(); diff --git a/src/Editor/WorldEditor/CInstancesModel.h b/src/Editor/WorldEditor/CInstancesModel.h index 5b810787..e17825a0 100644 --- a/src/Editor/WorldEditor/CInstancesModel.h +++ b/src/Editor/WorldEditor/CInstancesModel.h @@ -37,6 +37,7 @@ private: QList mTemplateList; QStringList mBaseItems; bool mShowColumnEnabled; + bool mAddingOrRemovingRows; public: explicit CInstancesModel(QObject *pParent = 0); @@ -53,13 +54,17 @@ public: void SetArea(CGameArea *pArea); void SetModelType(EInstanceModelType type); void SetShowColumnEnabled(bool Enabled); - void NodeCreated(CSceneNode *pNode); - void NodeDeleted(CSceneNode *pNode); CScriptLayer* IndexLayer(const QModelIndex& index) const; CScriptTemplate* IndexTemplate(const QModelIndex& index) const; CScriptObject* IndexObject(const QModelIndex& index) const; public slots: + void NodeAboutToBeCreated(); + void NodeCreated(CSceneNode *pNode); + void NodeAboutToBeDeleted(CSceneNode *pNode); + void NodeDeleted(); + + void PropertyModified(CScriptObject *pInst, IProperty *pProp); void InstancesLayerPreChange(); void InstancesLayerPostChange(const QList& rkInstanceList); diff --git a/src/Editor/WorldEditor/CLinkModel.cpp b/src/Editor/WorldEditor/CLinkModel.cpp index 572a1130..a0674f79 100644 --- a/src/Editor/WorldEditor/CLinkModel.cpp +++ b/src/Editor/WorldEditor/CLinkModel.cpp @@ -48,7 +48,7 @@ QVariant CLinkModel::data(const QModelIndex &index, int role) const case 0: // Column 0 - Target Object { u32 TargetID = (mType == eIncoming ? pLink->SenderID() : pLink->ReceiverID()); - CScriptObject *pTarget = mpObject->Area()->GetInstanceByID(TargetID); + CScriptObject *pTarget = mpObject->Area()->InstanceByID(TargetID); if (pTarget) { QString ObjType = QString("[%1] ").arg(UICommon::ToQString(pTarget->Template()->Name())); diff --git a/src/Editor/WorldEditor/CPoiMapModel.cpp b/src/Editor/WorldEditor/CPoiMapModel.cpp index d9d555e8..3a165462 100644 --- a/src/Editor/WorldEditor/CPoiMapModel.cpp +++ b/src/Editor/WorldEditor/CPoiMapModel.cpp @@ -69,7 +69,7 @@ QVariant CPoiMapModel::data(const QModelIndex& rkIndex, int Role) const if (rkIndex.row() < rowCount(QModelIndex())) { const CPoiToWorld::SPoiMap *pkMap = mpPoiToWorld->MapByIndex(rkIndex.row()); - CScriptObject *pPOI = mpArea->GetInstanceByID(pkMap->PoiID); + CScriptObject *pPOI = mpArea->InstanceByID(pkMap->PoiID); if (Role == Qt::DisplayRole) { diff --git a/src/Editor/WorldEditor/CTemplateListView.h b/src/Editor/WorldEditor/CTemplateListView.h new file mode 100644 index 00000000..0bc95cbf --- /dev/null +++ b/src/Editor/WorldEditor/CTemplateListView.h @@ -0,0 +1,124 @@ +#ifndef CTEMPLATELISTVIEW +#define CTEMPLATELISTVIEW + +#include "CTemplateMimeData.h" +#include "Editor/UICommon.h" +#include +#include +#include +#include + +class CTemplateListModel : public QAbstractListModel +{ + Q_OBJECT + CMasterTemplate *mpMaster; + QList 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(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 + diff --git a/src/Editor/WorldEditor/CTemplateMimeData.h b/src/Editor/WorldEditor/CTemplateMimeData.h new file mode 100644 index 00000000..e6d94e3b --- /dev/null +++ b/src/Editor/WorldEditor/CTemplateMimeData.h @@ -0,0 +1,20 @@ +#ifndef CTEMPLATEMIMEDATA_H +#define CTEMPLATEMIMEDATA_H + +#include +#include + +class CTemplateMimeData : public QMimeData +{ + Q_OBJECT + CScriptTemplate *mpTemplate; + +public: + CTemplateMimeData(CScriptTemplate *pTemplate) + : mpTemplate(pTemplate) + {} + + inline CScriptTemplate* Template() const { return mpTemplate; } +}; + +#endif // CTEMPLATEMIMEDATA_H diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index 7f0e0dbb..2cb625cf 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -1,17 +1,18 @@ #include "CWorldEditor.h" #include "ui_CWorldEditor.h" - #include "CConfirmUnlinkDialog.h" #include "CLayerEditor.h" +#include "CTemplateMimeData.h" #include "WModifyTab.h" #include "WInstancesTab.h" #include "Editor/CBasicViewport.h" +#include "Editor/CSelectionIterator.h" +#include "Editor/UICommon.h" #include "Editor/PropertyEdit/CPropertyView.h" #include "Editor/Widgets/WDraggableSpinBox.h" #include "Editor/Widgets/WVectorEditor.h" #include "Editor/Undo/UndoCommands.h" -#include "Editor/UICommon.h" #include #include @@ -38,7 +39,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) Log::Write("Creating World Editor"); ui->setupUi(this); - mSelectionNodeFlags = eScriptNode | eLightNode; + mpSelection->SetAllowedNodeTypes(eScriptNode | eLightNode); // Start refresh timer connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport())); @@ -51,6 +52,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) // Initialize UI stuff ui->MainViewport->SetScene(this, &mScene); + ui->CreateTabContents->SetEditor(this); ui->ModifyTabContents->SetEditor(this); ui->InstancesTabContents->SetEditor(this, &mScene); ui->TransformSpinBox->SetOrientation(Qt::Horizontal); @@ -66,6 +68,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) // Initialize actions addAction(ui->ActionIncrementGizmo); addAction(ui->ActionDecrementGizmo); + addAction(ui->ActionDelete); QAction *pToolBarUndo = mUndoStack.createUndoAction(this); 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->ActionLink, SIGNAL(toggled(bool)), this, SLOT(OnLinkButtonToggled(bool))); 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(ui->ActionSave, SIGNAL(triggered()), this, SLOT(Save())); @@ -100,6 +104,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) ui->CreateTabEditorProperties->SyncToEditor(this); ui->ModifyTabEditorProperties->SyncToEditor(this); ui->InstancesTabEditorProperties->SyncToEditor(this); + ui->MainViewport->setAcceptDrops(true); } CWorldEditor::~CWorldEditor() @@ -127,12 +132,7 @@ void CWorldEditor::closeEvent(QCloseEvent *pEvent) } } -bool CWorldEditor::eventFilter(QObject * /*pObj*/, QEvent * /*pEvent*/) -{ - return false; -} - -void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex) +void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) { ExitPickMode(); ui->MainViewport->ResetHover(); @@ -181,6 +181,7 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex) // Set up sidebar tabs CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpArea->Version()); + ui->CreateTabContents->SetMaster(pMaster); ui->InstancesTabContents->SetMaster(pMaster); // Set up dialogs @@ -192,8 +193,8 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex) if (CurrentGame() < eReturns) { - CStringTable *pAreaNameTable = mpWorld->GetAreaName(AreaIndex); - TWideString AreaName = pAreaNameTable ? pAreaNameTable->GetString("ENGL", 0) : (TWideString("!") + mpWorld->GetAreaInternalName(AreaIndex).ToUTF16()); + CStringTable *pAreaNameTable = mpWorld->GetAreaName(mpArea->WorldIndex()); + TWideString AreaName = pAreaNameTable ? pAreaNameTable->GetString("ENGL", 0) : (TWideString("!") + mpWorld->GetAreaInternalName(mpArea->WorldIndex()).ToUTF16()); if (AreaName.IsEmpty()) AreaName = "[Untitled Area]"; @@ -206,7 +207,7 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex) { QString LevelName; 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)); Log::Write("Loaded level: World " + mpWorld->Source() + " / Area " + mpArea->Source() + " (" + TO_TSTRING(LevelName) + ")"); @@ -238,7 +239,20 @@ bool CWorldEditor::CheckUnsavedChanges() return OkToClear; } +CSceneViewport* CWorldEditor::Viewport() const +{ + return ui->MainViewport; +} + // ************ PUBLIC SLOTS ************ +void CWorldEditor::NotifyNodeAboutToBeDeleted(CSceneNode *pNode) +{ + INodeEditor::NotifyNodeAboutToBeDeleted(pNode); + + if (ui->MainViewport->HoverNode() == pNode) + ui->MainViewport->ResetHover(); +} + bool CWorldEditor::Save() { TString Out = mpArea->FullSource(); @@ -273,9 +287,9 @@ void CWorldEditor::OnPropertyModified(IProperty *pProp) { bool EditorProperty = false; - if (!mSelection.isEmpty() && mSelection.front()->NodeType() == eScriptNode) + if (!mpSelection->IsEmpty() && mpSelection->Front()->NodeType() == eScriptNode) { - CScriptNode *pScript = static_cast(mSelection.front()); + CScriptNode *pScript = static_cast(mpSelection->Front()); pScript->PropertyModified(pProp); // Check editor property @@ -295,13 +309,13 @@ void CWorldEditor::OnPropertyModified(IProperty *pProp) CFileTemplate *pFile = static_cast(pProp->Template()); if (pFile->AcceptsExtension("CMDL") || pFile->AcceptsExtension("ANCS") || pFile->AcceptsExtension("CHAR")) - NotifySelectionModified(); + SelectionModified(); } else if (pProp->Type() == eCharacterProperty) - NotifySelectionModified(); + SelectionModified(); // 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 QVector Objects; - foreach (CSceneNode *pNode, mSelection) + for (CSelectionIterator It(mpSelection); It; ++It) { - if (pNode->NodeType() == eScriptNode) + if (It->NodeType() == eScriptNode) { - CScriptNode *pScript = static_cast(pNode); + CScriptNode *pScript = static_cast(*It); CScriptObject *pInst = pScript->Object(); 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 // 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(mSelection.front()); + CScriptNode *pNode = static_cast(mpSelection->Front()); CScriptObject *pInst = pNode->Object(); IProperty *pName = pInst->InstanceNameProperty(); @@ -363,16 +377,23 @@ void CWorldEditor::SetSelectionLayer(CScriptLayer *pLayer) { QList ScriptNodes; - foreach (CSceneNode *pNode, mSelection) + for (CSelectionIterator It(mpSelection); It; ++It) { - if (pNode->NodeType() == eScriptNode) - ScriptNodes << static_cast(pNode); + if (It->NodeType() == eScriptNode) + ScriptNodes << static_cast(*It); } if (!ScriptNodes.isEmpty()) 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() { // 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(); - if (pHoverNode && (pHoverNode->NodeType() & mSelectionNodeFlags)) + if (pHoverNode && mpSelection->IsAllowedType(pHoverNode)) StatusText = TO_QSTRING(pHoverNode->Name()); } } @@ -409,26 +430,26 @@ void CWorldEditor::UpdateGizmoUI() case CGizmo::eTranslate: if (mGizmoTransforming && mGizmo.HasTransformed()) spinBoxValue = mGizmo.TotalTranslation(); - else if (!mSelection.empty()) - spinBoxValue = mSelection.front()->AbsolutePosition(); + else if (!mpSelection->IsEmpty()) + spinBoxValue = mpSelection->Front()->AbsolutePosition(); break; case CGizmo::eRotate: if (mGizmoTransforming && mGizmo.HasTransformed()) spinBoxValue = mGizmo.TotalRotation(); - else if (!mSelection.empty()) - spinBoxValue = mSelection.front()->AbsoluteRotation().ToEuler(); + else if (!mpSelection->IsEmpty()) + spinBoxValue = mpSelection->Front()->AbsoluteRotation().ToEuler(); break; case CGizmo::eScale: if (mGizmoTransforming && mGizmo.HasTransformed()) spinBoxValue = mGizmo.TotalScale(); - else if (!mSelection.empty()) - spinBoxValue = mSelection.front()->AbsoluteScale(); + else if (!mpSelection->IsEmpty()) + spinBoxValue = mpSelection->Front()->AbsoluteScale(); break; } } - else if (!mSelection.empty()) spinBoxValue = mSelection.front()->AbsolutePosition(); + else if (!mpSelection->IsEmpty()) spinBoxValue = mpSelection->Front()->AbsolutePosition(); ui->TransformSpinBox->blockSignals(true); ui->TransformSpinBox->SetValue(spinBoxValue); @@ -439,26 +460,23 @@ void CWorldEditor::UpdateGizmoUI() if (!mGizmoTransforming) { // Set gizmo transform - if (!mSelection.empty()) + if (!mpSelection->IsEmpty()) { - mGizmo.SetPosition(mSelection.front()->AbsolutePosition()); - mGizmo.SetLocalRotation(mSelection.front()->AbsoluteRotation()); + mGizmo.SetPosition(mpSelection->Front()->AbsolutePosition()); + mGizmo.SetLocalRotation(mpSelection->Front()->AbsoluteRotation()); } } } void CWorldEditor::UpdateSelectionUI() { - // Update sidebar - ui->ModifyTabContents->GenerateUI(mSelection); - // Update selection info text QString SelectionText; - if (mSelection.size() == 1) - SelectionText = TO_QSTRING(mSelection.front()->Name()); - else if (mSelection.size() > 1) - SelectionText = QString("%1 objects selected").arg(mSelection.size()); + if (mpSelection->Size() == 1) + SelectionText = TO_QSTRING(mpSelection->Front()->Name()); + else if (mpSelection->Size() > 1) + SelectionText = QString("%1 objects selected").arg(mpSelection->Size()); QFontMetrics Metrics(ui->SelectionInfoLabel->font()); SelectionText = Metrics.elidedText(SelectionText, Qt::ElideRight, ui->SelectionInfoFrame->width() - 10); @@ -478,7 +496,7 @@ void CWorldEditor::UpdateCursor() if (ui->MainViewport->IsHoveringGizmo()) ui->MainViewport->SetCursorState(Qt::SizeAllCursor); - else if ((pHoverNode) && (pHoverNode->NodeType() & mSelectionNodeFlags)) + else if ((pHoverNode) && mpSelection->IsAllowedType(pHoverNode)) ui->MainViewport->SetCursorState(Qt::PointingHandCursor); else ui->MainViewport->SetCursorState(Qt::ArrowCursor); @@ -604,10 +622,10 @@ void CWorldEditor::OnUnlinkClicked() { QList SelectedScriptNodes; - foreach (CSceneNode *pNode, mSelection) + for (CSelectionIterator It(mpSelection); It; ++It) { - if (pNode->NodeType() == eScriptNode) - SelectedScriptNodes << static_cast(pNode); + if (It->NodeType() == eScriptNode) + SelectedScriptNodes << static_cast(*It); } if (!SelectedScriptNodes.isEmpty()) @@ -727,8 +745,8 @@ void CWorldEditor::UpdateCameraOrbit() { CCamera *pCamera = &ui->MainViewport->Camera(); - if (!mSelection.isEmpty()) - pCamera->SetOrbit(mSelectionBounds); + if (!mpSelection->IsEmpty()) + pCamera->SetOrbit(mpSelection->Bounds()); else if (mpArea) pCamera->SetOrbit(mpArea->AABox(), 1.5f); } @@ -745,34 +763,33 @@ void CWorldEditor::OnCameraSpeedChange(double speed) void CWorldEditor::OnTransformSpinBoxModified(CVector3f value) { - if (mSelection.empty()) return; + if (mpSelection->IsEmpty()) return; switch (mGizmo.Mode()) { // Use absolute position/rotation, but relative scale. (This way spinbox doesn't show preview multiplier) case CGizmo::eTranslate: { - CVector3f delta = value - mSelection.front()->AbsolutePosition(); - mUndoStack.push(new CTranslateNodeCommand(this, mSelection, delta, mTranslateSpace)); + CVector3f delta = value - mpSelection->Front()->AbsolutePosition(); + mUndoStack.push(new CTranslateNodeCommand(this, mpSelection->SelectedNodeList(), delta, mTranslateSpace)); break; } case CGizmo::eRotate: { - CQuaternion delta = CQuaternion::FromEuler(value) * mSelection.front()->AbsoluteRotation().Inverse(); - mUndoStack.push(new CRotateNodeCommand(this, mSelection, CVector3f::skZero, delta, mRotateSpace)); + CQuaternion delta = CQuaternion::FromEuler(value) * mpSelection->Front()->AbsoluteRotation().Inverse(); + mUndoStack.push(new CRotateNodeCommand(this, mpSelection->SelectedNodeList(), CVector3f::skZero, delta, mRotateSpace)); break; } case CGizmo::eScale: { - CVector3f delta = value / mSelection.front()->AbsoluteScale(); - mUndoStack.push(new CScaleNodeCommand(this, mSelection, CVector3f::skZero, delta)); + CVector3f delta = value / mpSelection->Front()->AbsoluteScale(); + mUndoStack.push(new CScaleNodeCommand(this, mpSelection->SelectedNodeList(), CVector3f::skZero, delta)); break; } } - RecalculateSelectionBounds(); UpdateGizmoUI(); } @@ -782,7 +799,7 @@ void CWorldEditor::OnTransformSpinBoxEdited(CVector3f) ui->TransformSpinBox->blockSignals(true); ui->MainViewport->setFocus(); ui->TransformSpinBox->blockSignals(false); - if (mSelection.empty()) return; + if (mpSelection->IsEmpty()) return; if (mGizmo.Mode() == CGizmo::eTranslate) mUndoStack.push(CTranslateNodeCommand::End()); else if (mGizmo.Mode() == CGizmo::eRotate) mUndoStack.push(CRotateNodeCommand::End()); diff --git a/src/Editor/WorldEditor/CWorldEditor.h b/src/Editor/WorldEditor/CWorldEditor.h index 5361055c..5e2f3e1f 100644 --- a/src/Editor/WorldEditor/CWorldEditor.h +++ b/src/Editor/WorldEditor/CWorldEditor.h @@ -5,6 +5,7 @@ #include "CPoiMapEditDialog.h" #include "Editor/INodeEditor.h" #include "Editor/CGizmo.h" +#include "Editor/CSceneViewport.h" #include #include @@ -47,21 +48,24 @@ public: explicit CWorldEditor(QWidget *parent = 0); ~CWorldEditor(); void closeEvent(QCloseEvent *pEvent); - bool eventFilter(QObject *pObj, QEvent *pEvent); - void SetArea(CWorld *pWorld, CGameArea *pArea, u32 AreaIndex); + void SetArea(CWorld *pWorld, CGameArea *pArea); bool CheckUnsavedChanges(); inline CGameArea* ActiveArea() const { return mpArea; } inline EGame CurrentGame() const { return mpArea ? mpArea->Version() : eUnknownVersion; } inline CLinkDialog* LinkDialog() const { return mpLinkDialog; } + CSceneViewport* Viewport() const; public slots: + virtual void NotifyNodeAboutToBeDeleted(CSceneNode *pNode); + bool Save(); void OnLinksModified(const QList& rkInstances); void OnPropertyModified(IProperty *pProp); void SetSelectionActive(bool Active); void SetSelectionInstanceNames(const QString& rkNewName, bool IsDone); void SetSelectionLayer(CScriptLayer *pLayer); + void DeleteSelection(); void UpdateStatusBar(); void UpdateGizmoUI(); @@ -116,7 +120,7 @@ signals: void InstancesLayerAboutToChange(); void InstancesLayerChanged(const QList& rkInstanceList); void InstanceLinksModified(const QList& rkInstances); - void PropertyModified(IProperty *pProp, bool IsEditorProperty); + void PropertyModified(CScriptObject *pInst, IProperty *pProp); }; #endif // CWORLDEDITOR_H diff --git a/src/Editor/WorldEditor/CWorldEditor.ui b/src/Editor/WorldEditor/CWorldEditor.ui index 9c639bd3..84238fdb 100644 --- a/src/Editor/WorldEditor/CWorldEditor.ui +++ b/src/Editor/WorldEditor/CWorldEditor.ui @@ -229,7 +229,7 @@ QTabWidget::Rounded - 1 + 0 @@ -268,17 +268,14 @@ - - - Qt::Vertical + + + + 0 + 1 + - - - 20 - 40 - - - + @@ -755,6 +752,14 @@ Edit POI to World Map + + + Delete + + + Del + + @@ -792,6 +797,12 @@
Editor/WorldEditor/WEditorProperties.h
1
+ + WCreateTab + QWidget +
Editor/WorldEditor/WCreateTab.h
+ 1 +
diff --git a/src/Editor/WorldEditor/WCreateTab.cpp b/src/Editor/WorldEditor/WCreateTab.cpp index 529bbc61..fe950800 100644 --- a/src/Editor/WorldEditor/WCreateTab.cpp +++ b/src/Editor/WorldEditor/WCreateTab.cpp @@ -1,5 +1,8 @@ #include "WCreateTab.h" #include "ui_WCreateTab.h" +#include "CTemplateMimeData.h" +#include "CWorldEditor.h" +#include "Editor/Undo/UndoCommands.h" WCreateTab::WCreateTab(QWidget *parent) : QWidget(parent), @@ -12,3 +15,47 @@ WCreateTab::~WCreateTab() { delete ui; } + +bool WCreateTab::eventFilter(QObject *pObj, QEvent *pEvent) +{ + if (pObj == mpEditor->Viewport()) + { + if (pEvent->type() == QEvent::DragEnter) + { + QDragEnterEvent *pDragEvent = static_cast(pEvent); + + if (qobject_cast(pDragEvent->mimeData())) + { + pDragEvent->acceptProposedAction(); + return true; + } + } + + else if (pEvent->type() == QEvent::Drop) + { + QDropEvent *pDropEvent = static_cast(pEvent); + const CTemplateMimeData *pMimeData = qobject_cast(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); +} diff --git a/src/Editor/WorldEditor/WCreateTab.h b/src/Editor/WorldEditor/WCreateTab.h index 78be581e..cff70a2c 100644 --- a/src/Editor/WorldEditor/WCreateTab.h +++ b/src/Editor/WorldEditor/WCreateTab.h @@ -1,6 +1,8 @@ #ifndef WCREATETAB_H #define WCREATETAB_H +#include "CWorldEditor.h" +#include #include namespace Ui { @@ -10,11 +12,14 @@ class WCreateTab; class WCreateTab : public QWidget { Q_OBJECT + CWorldEditor *mpEditor; public: explicit WCreateTab(QWidget *parent = 0); ~WCreateTab(); - + bool eventFilter(QObject *, QEvent *); + void SetEditor(CWorldEditor *pEditor); + void SetMaster(CMasterTemplate *pMaster); private: Ui::WCreateTab *ui; }; diff --git a/src/Editor/WorldEditor/WCreateTab.ui b/src/Editor/WorldEditor/WCreateTab.ui index 020c02cc..1113891d 100644 --- a/src/Editor/WorldEditor/WCreateTab.ui +++ b/src/Editor/WorldEditor/WCreateTab.ui @@ -6,14 +6,90 @@ 0 0 - 216 - 421 + 290 + 470 Form + + + 9 + + + 9 + + + 9 + + + 9 + + + + + Create Instance + + + + + + + + Spawn Layer: + + + + + + + + 1 + 0 + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + QAbstractItemView::SelectItems + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + + + + CTemplateListView + QListView +
Editor/WorldEditor/CTemplateListView.h
+
+
diff --git a/src/Editor/WorldEditor/WEditorProperties.cpp b/src/Editor/WorldEditor/WEditorProperties.cpp index b0f004ad..e0e0776b 100644 --- a/src/Editor/WorldEditor/WEditorProperties.cpp +++ b/src/Editor/WorldEditor/WEditorProperties.cpp @@ -65,7 +65,7 @@ void WEditorProperties::SyncToEditor(CWorldEditor *pEditor) connect(mpEditor, SIGNAL(SelectionModified()), this, SLOT(OnSelectionModified())); connect(mpEditor, SIGNAL(LayersModified()), this, SLOT(OnLayersModified())); connect(mpEditor, SIGNAL(InstancesLayerChanged(QList)), this, SLOT(OnInstancesLayerChanged(QList))); - connect(mpEditor, SIGNAL(PropertyModified(IProperty*,bool)), this, SLOT(OnPropertyModified(IProperty*,bool))); + connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), this, SLOT(OnPropertyModified(CScriptObject*,IProperty*))); OnLayersModified(); } @@ -100,16 +100,16 @@ void WEditorProperties::SetLayerComboBox() // ************ PUBLIC SLOTS ************ void WEditorProperties::OnSelectionModified() { - const QList& rkSelection = mpEditor->GetSelection(); - mpDisplayNode = (rkSelection.size() == 1 ? rkSelection.front() : nullptr); + CNodeSelection *pSelection = mpEditor->Selection(); + 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->setEnabled(false); mpInstanceNameLineEdit->setEnabled(false); - if (rkSelection.empty()) + if (pSelection->IsEmpty()) { mpInstanceInfoLabel->setText("
[No selection]"); mpInstanceNameLineEdit->clear(); @@ -121,7 +121,7 @@ void WEditorProperties::OnSelectionModified() } else { - mpInstanceInfoLabel->setText(QString("[%1 objects selected]").arg(rkSelection.size())); + mpInstanceInfoLabel->setText(QString("[%1 objects selected]").arg(pSelection->Size())); mpInstanceNameLineEdit->clear(); } } @@ -139,11 +139,11 @@ void WEditorProperties::OnSelectionModified() SetLayerComboBox(); } -void WEditorProperties::OnPropertyModified(IProperty* /*pProp*/, bool IsEditorProperty) +void WEditorProperties::OnPropertyModified(CScriptObject *pInstance, IProperty *pProp) { if (!mpInstanceNameLineEdit->hasFocus()) { - if (mpDisplayNode->NodeType() == eScriptNode && IsEditorProperty) + if (mpDisplayNode->NodeType() == eScriptNode && pInstance->IsEditorProperty(pProp)) UpdatePropertyValues(); } } diff --git a/src/Editor/WorldEditor/WEditorProperties.h b/src/Editor/WorldEditor/WEditorProperties.h index 50a40915..e21ba9b2 100644 --- a/src/Editor/WorldEditor/WEditorProperties.h +++ b/src/Editor/WorldEditor/WEditorProperties.h @@ -39,7 +39,7 @@ public: public slots: void OnSelectionModified(); - void OnPropertyModified(IProperty *pProp, bool IsEditorProperty); + void OnPropertyModified(CScriptObject *pInst, IProperty *pProp); void OnInstancesLayerChanged(const QList& rkNodeList); void OnLayersModified(); void UpdatePropertyValues(); diff --git a/src/Editor/WorldEditor/WModifyTab.cpp b/src/Editor/WorldEditor/WModifyTab.cpp index b9f79d41..49c7996e 100644 --- a/src/Editor/WorldEditor/WModifyTab.cpp +++ b/src/Editor/WorldEditor/WModifyTab.cpp @@ -64,18 +64,28 @@ void WModifyTab::SetEditor(CWorldEditor *pEditor) ui->PropertyView->SetEditor(mpWorldEditor); connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed())); connect(mpWorldEditor, SIGNAL(InstanceLinksModified(const QList&)), this, SLOT(OnInstanceLinksModified(const QList&))); + connect(mpWorldEditor->Selection(), SIGNAL(Modified()), this, SLOT(GenerateUI())); } -void WModifyTab::GenerateUI(QList& Selection) +void WModifyTab::ClearUI() +{ + ui->ObjectsTabWidget->hide(); + ui->PropertyView->SetInstance(nullptr); + ui->LightGroupBox->hide(); + mpSelectedNode = nullptr; +} + +// ************ PUBLIC SLOTS ************ +void WModifyTab::GenerateUI() { if (mIsPicking) 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 if (mpSelectedNode->NodeType() == eScriptNode) @@ -100,15 +110,6 @@ void WModifyTab::GenerateUI(QList& Selection) ClearUI(); } -void WModifyTab::ClearUI() -{ - ui->ObjectsTabWidget->hide(); - ui->PropertyView->SetInstance(nullptr); - ui->LightGroupBox->hide(); - mpSelectedNode = nullptr; -} - -// ************ PUBLIC SLOTS ************ void WModifyTab::OnInstanceLinksModified(const QList& rkInstances) { if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode) diff --git a/src/Editor/WorldEditor/WModifyTab.h b/src/Editor/WorldEditor/WModifyTab.h index cd6964f9..6d7e4edf 100644 --- a/src/Editor/WorldEditor/WModifyTab.h +++ b/src/Editor/WorldEditor/WModifyTab.h @@ -3,6 +3,7 @@ #include "CLinkDialog.h" #include "CLinkModel.h" +#include "Editor/CNodeSelection.h" #include #include @@ -36,10 +37,10 @@ public: explicit WModifyTab(QWidget *pParent = 0); ~WModifyTab(); void SetEditor(CWorldEditor *pEditor); - void GenerateUI(QList& Selection); void ClearUI(); public slots: + void GenerateUI(); void OnInstanceLinksModified(const QList& rkInstances); void OnWorldSelectionTransformed(); diff --git a/src/Math/CQuaternion.cpp b/src/Math/CQuaternion.cpp index 6de625eb..db001827 100644 --- a/src/Math/CQuaternion.cpp +++ b/src/Math/CQuaternion.cpp @@ -20,22 +20,22 @@ CQuaternion::CQuaternion(float _w, float _x, float _y, float _z) z = _z; } -CVector3f CQuaternion::XAxis() +CVector3f CQuaternion::XAxis() const { return (*this * CVector3f::skUnitX); } -CVector3f CQuaternion::YAxis() +CVector3f CQuaternion::YAxis() const { return (*this * CVector3f::skUnitY); } -CVector3f CQuaternion::ZAxis() +CVector3f CQuaternion::ZAxis() const { return (*this * CVector3f::skUnitZ); } -CQuaternion CQuaternion::Inverse() +CQuaternion CQuaternion::Inverse() const { float fNorm = (w * w) + (x * x) + (y * y) + (z * z); @@ -48,7 +48,7 @@ CQuaternion CQuaternion::Inverse() return CQuaternion::skZero; } -CVector3f CQuaternion::ToEuler() +CVector3f CQuaternion::ToEuler() const { // 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, diff --git a/src/Math/CQuaternion.h b/src/Math/CQuaternion.h index ddcc0e5b..659bef1e 100644 --- a/src/Math/CQuaternion.h +++ b/src/Math/CQuaternion.h @@ -11,11 +11,11 @@ public: CQuaternion(); CQuaternion(float _w, float _x, float _y, float _z); - CVector3f XAxis(); - CVector3f YAxis(); - CVector3f ZAxis(); - CQuaternion Inverse(); - CVector3f ToEuler(); + CVector3f XAxis() const; + CVector3f YAxis() const; + CVector3f ZAxis() const; + CQuaternion Inverse() const; + CVector3f ToEuler() const; // Operators CVector3f operator*(const CVector3f& vec) const;