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

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

View File

@ -58,6 +58,8 @@ void Write(const TString& rkMessage)
fprintf(gpLogFile, "[%08.3f] %s\n", Time, *rkMessage);
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)

View File

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

View File

@ -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;
}

View File

@ -9,12 +9,14 @@
#include "Core/Resource/Model/CModel.h"
#include "Core/Resource/Model/CStaticModel.h"
#include <Common/types.h>
#include <Math/CQuaternion.h>
#include <Math/CTransform4f.h>
#include <unordered_map>
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<u32, CScriptObject*> mObjectMap;
// Collision
CCollisionMeshGroup *mCollision;
CCollisionMeshGroup *mpCollision;
// Lights
std::vector<std::vector<CLight*>> 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

View File

@ -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

View File

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

View File

@ -16,12 +16,12 @@ CWorld::~CWorld()
{
}
void CWorld::SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex)
void CWorld::SetAreaLayerInfo(CGameArea *pArea)
{
// The AreaIndex parameter is a placeholder until an improved world loader is implemented.
// 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++)
{

View File

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

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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; }

View File

@ -8,13 +8,15 @@
class CScriptLayer
{
CGameArea *mpArea;
TString mLayerName;
bool mActive;
bool mVisible;
std::vector<CScriptObject*> mInstances;
public:
CScriptLayer()
: mLayerName("New Layer")
CScriptLayer(CGameArea *pArea)
: mpArea(pArea)
, mLayerName("New Layer")
, mActive(true)
, mVisible(true)
{
@ -27,8 +29,16 @@ public:
}
// Data Manipulation
void AddInstance(CScriptObject *pObject)
void AddInstance(CScriptObject *pObject, u32 Index = -1)
{
if (Index != -1 && Index < mInstances.size())
{
auto it = mInstances.begin();
std::advance(it, Index);
mInstances.insert(it, pObject);
}
else
mInstances.push_back(pObject);
}
@ -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); }
};

View File

@ -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)

View File

@ -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;

View File

@ -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<CScriptNode*>(pNode);
auto ScriptMapIt = mScriptNodeMap.find(pScript->Object()->InstanceID());
if (ScriptMapIt != mScriptNodeMap.end())
mScriptNodeMap.erase(ScriptMapIt);
switch (pScript->Object()->ObjectTypeID())
{
case 0x4E:
case 0x52454141:
for (auto it = mAreaAttributesObjects.begin(); it != mAreaAttributesObjects.end(); it++)
{
if ((*it).Instance() == pScript->Object())
{
mAreaAttributesObjects.erase(it);
break;
}
}
break;
}
}
pNode->Unparent();
delete pNode;
mNumNodes--;
}
void CScene::SetActiveArea(CGameArea *pArea)
{
// 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<CSceneNode*>& 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<CSceneNode*>& 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);

View File

@ -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();

View File

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

View File

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

View File

@ -1,24 +1,126 @@
#ifndef CNODESELECTION_H
#define CNODESELECTION_H
#include <Core/Scene/CSceneIterator.h>
#include <Core/Scene/CSceneNode.h>
#include <list>
#include <Core/Scene/CScriptNode.h>
#include <QList>
#include <QObject>
class CSceneSelection
class CNodeSelection : public QObject
{
CScene *mpScene;
std::vector<CSceneNode*> mSelectedNodes;
Q_OBJECT
FNodeFlags mAllowedNodes;
QList<CSceneNode*> 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<CSceneNode*>& rkList)
{
blockSignals(true);
Clear();
foreach (CSceneNode *pNode, rkList)
SelectNode(pNode);
blockSignals(false);
mBoundsDirty = true;
emit Modified();
}
CAABox Bounds() const
{
if (mBoundsDirty)
{
mCachedBounds = CAABox::skInfinite;
foreach (CSceneNode *pNode, mSelectedNodes)
{
mCachedBounds.ExpandBounds(pNode->AABox());
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
if (pScript->HasPreviewVolume())
mCachedBounds.ExpandBounds(pScript->PreviewVolumeAABox());
}
}
mBoundsDirty = false;
}
return mCachedBounds;
}
inline u32 Size() const { return mSelectedNodes.size(); }
inline bool IsEmpty() const { return Size() == 0; }
inline CSceneNode* At(u32 Index) const { return mSelectedNodes[Index]; }
inline CSceneNode* Front() const { return mSelectedNodes.front(); }
inline CSceneNode* Back() const { return mSelectedNodes.back(); }
inline CSceneNode* operator[](u32 Index) const { return mSelectedNodes[Index]; }
inline void UpdateBounds() { mBoundsDirty = true; }
inline void SetAllowedNodeTypes(FNodeFlags Types) { mAllowedNodes = Types; }
inline bool IsAllowedType(ENodeType Type) const { return (mAllowedNodes & Type) != 0; }
inline bool IsAllowedType(CSceneNode *pNode) const { return (mAllowedNodes & pNode->NodeType()) != 0; }
inline QList<CSceneNode*> SelectedNodeList() const { return mSelectedNodes; }
signals:
void Modified();
};
#endif // CNODESELECTION_H

View File

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

View File

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

View File

@ -183,8 +183,9 @@ void CStartWindow::on_LaunchWorldEditorButton_clicked()
else
{
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);

View File

@ -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 += \

View File

@ -1,11 +1,12 @@
#include "INodeEditor.h"
#include "CSelectionIterator.h"
#include "Editor/Undo/UndoCommands.h"
#include <QMouseEvent>
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<CScriptNode*>(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<CSceneNode*>& INodeEditor::GetSelection() const
CNodeSelection* INodeEditor::Selection() const
{
return mSelection;
return mpSelection;
}
void INodeEditor::EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor /*= Qt::CrossCursor*/)
@ -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);

View File

@ -2,6 +2,7 @@
#define INODEEDITOR_H
#include "CGizmo.h"
#include "CNodeSelection.h"
#include <Math/ETransformSpace.h>
#include <Core/Scene/CScene.h>
@ -23,9 +24,7 @@ protected:
// Node management
CScene mScene;
QList<CSceneNode*> 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<CSceneNode*>& 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;

View File

@ -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));
}

View File

@ -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:

View File

@ -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)

View File

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

View File

@ -7,14 +7,18 @@
class CClearSelectionCommand : public IUndoCommand
{
INodeEditor *mpEditor;
QList<CSceneNode*> mSelectionState;
QList<CSceneNode*> *mpSelection;
QList<CSceneNode*> mOldSelection;
CNodeSelection *mpSelection;
public:
CClearSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& 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; }
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,13 +7,17 @@
class CDeselectNodeCommand : public IUndoCommand
{
INodeEditor *mpEditor;
CSceneNode *mpNode;
QList<CSceneNode*> *mpSelection;
CNodeSelection *mpSelection;
public:
CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& 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; }
};

View File

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

View File

@ -7,17 +7,27 @@
class CInvertSelectionCommand : public IUndoCommand
{
INodeEditor *mpEditor;
CNodeSelection *mpSelection;
QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mNewSelection;
QList<CSceneNode*> *mpSelection;
public:
CInvertSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& rSelection, CScene *pScene, FNodeFlags NodeFlags);
~CInvertSelectionCommand();
void undo();
void redo();
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

View File

@ -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();
}

View File

@ -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();
}

View File

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

View File

@ -7,16 +7,22 @@
class CSelectAllCommand : public IUndoCommand
{
INodeEditor *mpEditor;
QList<CSceneNode*> mOldSelection;
QList<CSceneNode*> mNewSelection;
QList<CSceneNode*> *mpSelection;
CNodeSelection *mpSelection;
public:
CSelectAllCommand(INodeEditor *pEditor, QList<CSceneNode*>& 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; }
};

View File

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

View File

@ -7,13 +7,18 @@
class CSelectNodeCommand : public IUndoCommand
{
INodeEditor *mpEditor;
CSceneNode *mpNode;
QList<CSceneNode*> *mpSelection;
CNodeSelection *mpSelection;
public:
CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& 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; }
};

View File

@ -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();
}

View File

@ -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"

View File

@ -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<CScriptNode*>)), this, SLOT(InstancesLayerPostChange(QList<CScriptNode*>)));
}
@ -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<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
pObj->Template()->SortObjects();
if (pObj->Template()->NumObjects() == 1)
{
mTemplateList << pObj->Template();
qSort(mTemplateList.begin(), mTemplateList.end(), SortTemplatesAlphabetical);
emit layoutChanged();
}
}
}
}
void CInstancesModel::NodeDeleted(CSceneNode *pNode)
{
if (mModelType = eTypes)
{
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
if (pObj->Template()->NumObjects() == 0)
{
for (auto it = mTemplateList.begin(); it != mTemplateList.end(); it++)
{
if (*it == pObj->Template())
{
mTemplateList.erase(it);
break;
}
}
emit layoutChanged();
}
}
}
}
CScriptLayer* CInstancesModel::IndexLayer(const QModelIndex& index) const
{
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<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
pObj->Template()->SortObjects();
if (pObj->Template()->NumObjects() == 1)
{
QModelIndex ScriptRootIdx = index(0, 0, QModelIndex());
int NewIndex = 0;
for (; NewIndex < mTemplateList.size(); NewIndex++)
{
if (mTemplateList[NewIndex]->Name() > pObj->Template()->Name())
break;
}
beginInsertRows(ScriptRootIdx, NewIndex, NewIndex);
mTemplateList.insert(NewIndex, pObj->Template());
endInsertRows();
}
}
}
emit layoutChanged();
mAddingOrRemovingRows = false;
}
void CInstancesModel::NodeAboutToBeDeleted(CSceneNode *pNode)
{
if (!mAddingOrRemovingRows)
{
emit layoutAboutToBeChanged();
mAddingOrRemovingRows = true;
}
if (mModelType == eTypes)
{
if (pNode->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Object();
if (pObj->Template()->NumObjects() <= 1)
{
QModelIndex ScriptRootIdx = index(0, 0, QModelIndex());
int TempIdx = mTemplateList.indexOf(pObj->Template());
beginRemoveRows(ScriptRootIdx, TempIdx, TempIdx);
mTemplateList.removeOne(pObj->Template());
endRemoveRows();
}
}
}
}
void CInstancesModel::NodeDeleted()
{
emit layoutChanged();
mAddingOrRemovingRows = false;
}
void CInstancesModel::PropertyModified(CScriptObject *pInst, IProperty *pProp)
{
if (pInst->InstanceNameProperty() == pProp)
{
QModelIndex ScriptRoot = index(0, 0, QModelIndex());
if (mModelType == eLayers)
{
u32 Index = pInst->Layer()->AreaIndex();
QModelIndex LayerIndex = index(Index, 0, ScriptRoot);
QModelIndex InstIndex = index(pInst->LayerIndex(), 0, LayerIndex);
emit dataChanged(InstIndex, InstIndex);
}
else
{
u32 Index = mTemplateList.indexOf(pInst->Template());
QModelIndex TempIndex = index(Index, 0, ScriptRoot);
QList<CScriptObject*> InstList = QList<CScriptObject*>::fromStdList(pInst->Template()->ObjectList());
u32 InstIdx = InstList.indexOf(pInst);
QModelIndex InstIndex = index(InstIdx, 0, TempIndex);
emit dataChanged(InstIndex, InstIndex);
}
}
}
void CInstancesModel::InstancesLayerPreChange()
{
// 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();

View File

@ -37,6 +37,7 @@ private:
QList<CScriptTemplate*> 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<CScriptNode*>& rkInstanceList);

View File

@ -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()));

View File

@ -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)
{

View File

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

View File

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

View File

@ -1,17 +1,18 @@
#include "CWorldEditor.h"
#include "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 <Core/Render/CDrawUtil.h>
#include <Core/Resource/Cooker/CAreaCooker.h>
@ -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<CScriptNode*>(mSelection.front());
CScriptNode *pScript = static_cast<CScriptNode*>(mpSelection->Front());
pScript->PropertyModified(pProp);
// Check editor property
@ -295,13 +309,13 @@ void CWorldEditor::OnPropertyModified(IProperty *pProp)
CFileTemplate *pFile = static_cast<CFileTemplate*>(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<CScriptObject*> Objects;
foreach (CSceneNode *pNode, mSelection)
for (CSelectionIterator It(mpSelection); It; ++It)
{
if (pNode->NodeType() == eScriptNode)
if (It->NodeType() == eScriptNode)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptNode *pScript = static_cast<CScriptNode*>(*It);
CScriptObject *pInst = pScript->Object();
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<CScriptNode*>(mSelection.front());
CScriptNode *pNode = static_cast<CScriptNode*>(mpSelection->Front());
CScriptObject *pInst = pNode->Object();
IProperty *pName = pInst->InstanceNameProperty();
@ -363,16 +377,23 @@ void CWorldEditor::SetSelectionLayer(CScriptLayer *pLayer)
{
QList<CScriptNode*> ScriptNodes;
foreach (CSceneNode *pNode, mSelection)
for (CSelectionIterator It(mpSelection); It; ++It)
{
if (pNode->NodeType() == eScriptNode)
ScriptNodes << static_cast<CScriptNode*>(pNode);
if (It->NodeType() == eScriptNode)
ScriptNodes << static_cast<CScriptNode*>(*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<CScriptNode*> SelectedScriptNodes;
foreach (CSceneNode *pNode, mSelection)
for (CSelectionIterator It(mpSelection); It; ++It)
{
if (pNode->NodeType() == eScriptNode)
SelectedScriptNodes << static_cast<CScriptNode*>(pNode);
if (It->NodeType() == eScriptNode)
SelectedScriptNodes << static_cast<CScriptNode*>(*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());

View File

@ -5,6 +5,7 @@
#include "CPoiMapEditDialog.h"
#include "Editor/INodeEditor.h"
#include "Editor/CGizmo.h"
#include "Editor/CSceneViewport.h"
#include <Common/CTimer.h>
#include <Common/EKeyInputs.h>
@ -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<CScriptObject*>& 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<CScriptNode*>& rkInstanceList);
void InstanceLinksModified(const QList<CScriptObject*>& rkInstances);
void PropertyModified(IProperty *pProp, bool IsEditorProperty);
void PropertyModified(CScriptObject *pInst, IProperty *pProp);
};
#endif // CWORLDEDITOR_H

View File

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

View File

@ -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<QDragEnterEvent*>(pEvent);
if (qobject_cast<const CTemplateMimeData*>(pDragEvent->mimeData()))
{
pDragEvent->acceptProposedAction();
return true;
}
}
else if (pEvent->type() == QEvent::Drop)
{
QDropEvent *pDropEvent = static_cast<QDropEvent*>(pEvent);
const CTemplateMimeData *pMimeData = qobject_cast<const CTemplateMimeData*>(pDropEvent->mimeData());
if (pMimeData)
{
CVector3f SpawnPoint = mpEditor->Viewport()->HoverPoint();
CCreateInstanceCommand *pCmd = new CCreateInstanceCommand(mpEditor, pMimeData->Template(), mpEditor->ActiveArea()->GetScriptLayer(0), SpawnPoint);
mpEditor->UndoStack()->push(pCmd);
return true;
}
}
}
return false;
}
void WCreateTab::SetEditor(CWorldEditor *pEditor)
{
mpEditor = pEditor;
pEditor->Viewport()->installEventFilter(this);
}
void WCreateTab::SetMaster(CMasterTemplate *pMaster)
{
ui->TemplateView->SetMaster(pMaster);
}

View File

@ -1,6 +1,8 @@
#ifndef WCREATETAB_H
#define WCREATETAB_H
#include "CWorldEditor.h"
#include <Core/Resource/Script/CMasterTemplate.h>
#include <QWidget>
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;
};

View File

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

View File

@ -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<CScriptNode*>)), this, SLOT(OnInstancesLayerChanged(QList<CScriptNode*>)));
connect(mpEditor, SIGNAL(PropertyModified(IProperty*,bool)), this, SLOT(OnPropertyModified(IProperty*,bool)));
connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), this, SLOT(OnPropertyModified(CScriptObject*,IProperty*)));
OnLayersModified();
}
@ -100,16 +100,16 @@ void WEditorProperties::SetLayerComboBox()
// ************ PUBLIC SLOTS ************
void WEditorProperties::OnSelectionModified()
{
const QList<CSceneNode*>& 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("<i>[No selection]</i>");
mpInstanceNameLineEdit->clear();
@ -121,7 +121,7 @@ void WEditorProperties::OnSelectionModified()
}
else
{
mpInstanceInfoLabel->setText(QString("<i>[%1 objects selected]</i>").arg(rkSelection.size()));
mpInstanceInfoLabel->setText(QString("<i>[%1 objects selected]</i>").arg(pSelection->Size()));
mpInstanceNameLineEdit->clear();
}
}
@ -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();
}
}

View File

@ -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<CScriptNode*>& rkNodeList);
void OnLayersModified();
void UpdatePropertyValues();

View File

@ -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<CScriptObject*>&)), this, SLOT(OnInstanceLinksModified(const QList<CScriptObject*>&)));
connect(mpWorldEditor->Selection(), SIGNAL(Modified()), this, SLOT(GenerateUI()));
}
void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection)
void WModifyTab::ClearUI()
{
ui->ObjectsTabWidget->hide();
ui->PropertyView->SetInstance(nullptr);
ui->LightGroupBox->hide();
mpSelectedNode = nullptr;
}
// ************ PUBLIC SLOTS ************
void WModifyTab::GenerateUI()
{
if (mIsPicking)
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<CSceneNode*>& Selection)
ClearUI();
}
void WModifyTab::ClearUI()
{
ui->ObjectsTabWidget->hide();
ui->PropertyView->SetInstance(nullptr);
ui->LightGroupBox->hide();
mpSelectedNode = nullptr;
}
// ************ PUBLIC SLOTS ************
void WModifyTab::OnInstanceLinksModified(const QList<CScriptObject*>& rkInstances)
{
if (mpSelectedNode && mpSelectedNode->NodeType() == eScriptNode)

View File

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

View File

@ -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,

View File

@ -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;