Added support for script instance cloning
This commit is contained in:
parent
3d6993a1ec
commit
99a64342e9
|
@ -113,15 +113,16 @@ public:
|
|||
}
|
||||
|
||||
// Accessors
|
||||
u32 State() const { return mStateID; }
|
||||
u32 Message() const { return mMessageID; }
|
||||
u32 SenderID() const { return mSenderID; }
|
||||
u32 ReceiverID() const { return mReceiverID; }
|
||||
CScriptObject* Sender() const { return mpArea->InstanceByID(mSenderID); }
|
||||
CScriptObject* Receiver() const { return mpArea->InstanceByID(mReceiverID); }
|
||||
inline CGameArea* Area() const { return mpArea; }
|
||||
inline u32 State() const { return mStateID; }
|
||||
inline u32 Message() const { return mMessageID; }
|
||||
inline u32 SenderID() const { return mSenderID; }
|
||||
inline u32 ReceiverID() const { return mReceiverID; }
|
||||
inline CScriptObject* Sender() const { return mpArea->InstanceByID(mSenderID); }
|
||||
inline CScriptObject* Receiver() const { return mpArea->InstanceByID(mReceiverID); }
|
||||
|
||||
void SetState(u32 StateID) { mStateID = StateID; }
|
||||
void SetMessage(u32 MessageID) { mMessageID = MessageID; }
|
||||
inline void SetState(u32 StateID) { mStateID = StateID; }
|
||||
inline void SetMessage(u32 MessageID) { mMessageID = MessageID; }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -99,13 +99,8 @@ void CPropertyStruct::Copy(const IProperty *pkProp)
|
|||
{
|
||||
const CPropertyStruct *pkSource = static_cast<const CPropertyStruct*>(pkProp);
|
||||
|
||||
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
|
||||
delete *it;
|
||||
|
||||
mProperties.resize(pkSource->mProperties.size());
|
||||
|
||||
for (u32 iSub = 0; iSub < mProperties.size(); iSub++)
|
||||
mProperties[iSub] = pkSource->mProperties[iSub]->Clone(Instance(), this);
|
||||
mProperties[iSub]->Copy(pkSource->mProperties[iSub]);
|
||||
}
|
||||
|
||||
bool CPropertyStruct::ShouldCook()
|
||||
|
@ -196,6 +191,15 @@ CPropertyStruct* CPropertyStruct::StructByIDString(const TIDString& rkStr) const
|
|||
}
|
||||
|
||||
// ************ CArrayProperty ************
|
||||
void CArrayProperty::Copy(const IProperty *pkProp)
|
||||
{
|
||||
const CArrayProperty *pkSource = static_cast<const CArrayProperty*>(pkProp);
|
||||
Resize(pkSource->Count());
|
||||
|
||||
for (u32 iSub = 0; iSub < mProperties.size(); iSub++)
|
||||
mProperties[iSub]->Copy(pkSource->mProperties[iSub]);
|
||||
}
|
||||
|
||||
bool CArrayProperty::ShouldCook()
|
||||
{
|
||||
return (mpTemplate->CookPreference() == eNeverCook ? false : true);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef IPROPERTY
|
||||
#define IPROPERTY
|
||||
|
||||
/* This header file declares some classes used to track script object properties
|
||||
* IProperty, TTypedProperty (and typedefs), CPropertyStruct, and CArrayProperty */
|
||||
#include "EPropertyType.h"
|
||||
#include "IPropertyValue.h"
|
||||
#include "Core/Resource/CResource.h"
|
||||
|
@ -21,7 +19,6 @@ typedef TString TIDString;
|
|||
|
||||
/*
|
||||
* IProperty is the base class, containing just some virtual function definitions
|
||||
* Virtual destructor is mainly there to make cleanup easy; don't need to cast to delete
|
||||
*/
|
||||
class IProperty
|
||||
{
|
||||
|
@ -69,6 +66,15 @@ public:
|
|||
/*
|
||||
* TTypedProperty is a template subclass for actual properties.
|
||||
*/
|
||||
#define IMPLEMENT_PROPERTY_CLONE(ClassName) \
|
||||
virtual IProperty* Clone(CScriptObject *pInstance, CPropertyStruct *pParent) const \
|
||||
{ \
|
||||
if (!pParent) pParent = mpParent; \
|
||||
ClassName *pOut = new ClassName(mpTemplate, pInstance, pParent); \
|
||||
pOut->Copy(this); \
|
||||
return pOut; \
|
||||
}
|
||||
|
||||
template <typename ValueType, EPropertyType TypeEnum, class ValueClass>
|
||||
class TTypedProperty : public IProperty
|
||||
{
|
||||
|
@ -94,14 +100,7 @@ public:
|
|||
mValue.Set(pkCast->mValue.Get());
|
||||
}
|
||||
|
||||
virtual TTypedProperty* Clone(class CScriptObject *pInstance, CPropertyStruct *pParent) const
|
||||
{
|
||||
if (!pParent) pParent = mpParent;
|
||||
|
||||
TTypedProperty *pOut = new TTypedProperty(mpTemplate, pInstance, pParent);
|
||||
pOut->Copy(this);
|
||||
return pOut;
|
||||
}
|
||||
IMPLEMENT_PROPERTY_CLONE(TTypedProperty)
|
||||
|
||||
virtual bool Matches(const IProperty *pkProp) const
|
||||
{
|
||||
|
@ -138,6 +137,7 @@ class TStringProperty : public TTypedProperty<TString, eStringProperty, CStringV
|
|||
{
|
||||
public:
|
||||
IMPLEMENT_PROPERTY_CTORS(TStringProperty, TString)
|
||||
IMPLEMENT_PROPERTY_CLONE(TStringProperty)
|
||||
virtual bool MatchesDefault() { return Get().IsEmpty(); }
|
||||
virtual bool ShouldCook() { return true; }
|
||||
};
|
||||
|
@ -146,6 +146,7 @@ class TFileProperty : public TTypedProperty<CResourceInfo, eFileProperty, CFileV
|
|||
{
|
||||
public:
|
||||
IMPLEMENT_PROPERTY_CTORS(TFileProperty, CResourceInfo)
|
||||
IMPLEMENT_PROPERTY_CLONE(TFileProperty)
|
||||
virtual bool MatchesDefault() { return !Get().IsValid(); }
|
||||
virtual bool ShouldCook() { return true; }
|
||||
};
|
||||
|
@ -154,6 +155,7 @@ class TCharacterProperty : public TTypedProperty<CAnimationParameters, eCharacte
|
|||
{
|
||||
public:
|
||||
IMPLEMENT_PROPERTY_CTORS(TCharacterProperty, CAnimationParameters)
|
||||
IMPLEMENT_PROPERTY_CLONE(TCharacterProperty)
|
||||
virtual bool MatchesDefault() { return Get().AnimSet() == nullptr; }
|
||||
virtual bool ShouldCook() { return true; }
|
||||
};
|
||||
|
@ -162,6 +164,7 @@ class TMayaSplineProperty : public TTypedProperty<std::vector<u8>, eMayaSplinePr
|
|||
{
|
||||
public:
|
||||
IMPLEMENT_PROPERTY_CTORS(TMayaSplineProperty, std::vector<u8>)
|
||||
IMPLEMENT_PROPERTY_CLONE(TMayaSplineProperty)
|
||||
virtual bool MatchesDefault() { return Get().empty(); }
|
||||
};
|
||||
|
||||
|
@ -256,6 +259,8 @@ public:
|
|||
EPropertyType Type() const { return eArrayProperty; }
|
||||
static inline EPropertyType StaticType() { return eArrayProperty; }
|
||||
|
||||
virtual void Copy(const IProperty *pkProp);
|
||||
|
||||
virtual IProperty* Clone(CScriptObject *pInstance, CPropertyStruct *pParent) const
|
||||
{
|
||||
if (!pParent) pParent = mpParent;
|
||||
|
|
|
@ -362,7 +362,7 @@ bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera)
|
|||
if (!mHasTransformed && (mDeltaTranslation != CVector3f::skZero))
|
||||
mHasTransformed = true;
|
||||
|
||||
return true;
|
||||
return mHasTransformed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,7 +415,7 @@ bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera)
|
|||
if (!mHasTransformed && (rotAmount != 0.f))
|
||||
mHasTransformed = true;
|
||||
|
||||
return true;
|
||||
return mHasTransformed;
|
||||
}
|
||||
|
||||
// Scale
|
||||
|
@ -484,7 +484,7 @@ bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera)
|
|||
if (!mHasTransformed && (scaleAmount != 1.f))
|
||||
mHasTransformed = true;
|
||||
|
||||
return true;
|
||||
return mHasTransformed;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -154,7 +154,8 @@ HEADERS += \
|
|||
WorldEditor/CTemplateMimeData.h \
|
||||
WorldEditor/CTemplateListView.h \
|
||||
CSelectionIterator.h \
|
||||
Undo/ObjReferences.h
|
||||
Undo/ObjReferences.h \
|
||||
Undo/CCloneSelectionCommand.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
@ -211,7 +212,8 @@ SOURCES += \
|
|||
Undo/CDeleteLinksCommand.cpp \
|
||||
Undo/CEditLinkCommand.cpp \
|
||||
Undo/CDeleteSelectionCommand.cpp \
|
||||
Undo/CCreateInstanceCommand.cpp
|
||||
Undo/CCreateInstanceCommand.cpp \
|
||||
Undo/CCloneSelectionCommand.cpp
|
||||
|
||||
# UI Files
|
||||
FORMS += \
|
||||
|
|
|
@ -13,6 +13,7 @@ INodeEditor::INodeEditor(QWidget *pParent)
|
|||
, mGizmoTransforming(false)
|
||||
, mTranslateSpace(eWorldTransform)
|
||||
, mRotateSpace(eWorldTransform)
|
||||
, mCloneState(eNotCloning)
|
||||
{
|
||||
// Create undo actions
|
||||
QAction *pUndoAction = mUndoStack.createUndoAction(this);
|
||||
|
@ -85,6 +86,7 @@ bool INodeEditor::IsGizmoVisible()
|
|||
void INodeEditor::BeginGizmoTransform()
|
||||
{
|
||||
mGizmoTransforming = true;
|
||||
if ((qApp->keyboardModifiers() & Qt::ShiftModifier) != 0) mCloneState = eReadyToClone;
|
||||
|
||||
foreach (QAction *pAction, mGizmoActions)
|
||||
pAction->setEnabled(false);
|
||||
|
@ -97,12 +99,20 @@ void INodeEditor::EndGizmoTransform()
|
|||
foreach (QAction *pAction, mGizmoActions)
|
||||
pAction->setEnabled(true);
|
||||
|
||||
if (mGizmo.Mode() == CGizmo::eTranslate)
|
||||
mUndoStack.push(CTranslateNodeCommand::End());
|
||||
else if (mGizmo.Mode() == CGizmo::eRotate)
|
||||
mUndoStack.push(CRotateNodeCommand::End());
|
||||
else if (mGizmo.Mode() == CGizmo::eScale)
|
||||
mUndoStack.push(CScaleNodeCommand::End());
|
||||
if (mGizmo.HasTransformed())
|
||||
{
|
||||
if (mGizmo.Mode() == CGizmo::eTranslate)
|
||||
mUndoStack.push(CTranslateNodeCommand::End());
|
||||
else if (mGizmo.Mode() == CGizmo::eRotate)
|
||||
mUndoStack.push(CRotateNodeCommand::End());
|
||||
else if (mGizmo.Mode() == CGizmo::eScale)
|
||||
mUndoStack.push(CScaleNodeCommand::End());
|
||||
}
|
||||
|
||||
if (mCloneState == eCloning)
|
||||
mUndoStack.endMacro();
|
||||
|
||||
mCloneState = eNotCloning;
|
||||
}
|
||||
|
||||
ETransformSpace INodeEditor::CurrentTransformSpace()
|
||||
|
@ -251,6 +261,13 @@ void INodeEditor::OnSelectionModified()
|
|||
|
||||
void INodeEditor::OnGizmoMoved()
|
||||
{
|
||||
if (mCloneState == eReadyToClone)
|
||||
{
|
||||
mUndoStack.beginMacro("Clone");
|
||||
mUndoStack.push(new CCloneSelectionCommand(this));
|
||||
mCloneState = eCloning;
|
||||
}
|
||||
|
||||
switch (mGizmo.Mode())
|
||||
{
|
||||
case CGizmo::eTranslate:
|
||||
|
|
|
@ -34,6 +34,7 @@ protected:
|
|||
bool mGizmoTransforming;
|
||||
ETransformSpace mTranslateSpace;
|
||||
ETransformSpace mRotateSpace;
|
||||
enum { eNotCloning, eReadyToClone, eCloning } mCloneState;
|
||||
|
||||
// Gizmo widgets
|
||||
QActionGroup *mpGizmoGroup;
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
#include "CCloneSelectionCommand.h"
|
||||
#include "Editor/CSelectionIterator.h"
|
||||
|
||||
CCloneSelectionCommand::CCloneSelectionCommand(INodeEditor *pEditor)
|
||||
: IUndoCommand("Clone")
|
||||
, mpEditor(qobject_cast<CWorldEditor*>(pEditor)) // todo: fix this! bad assumption! (clone handling code is in INodeEditor but active area is in CWorldEditor)
|
||||
{
|
||||
mOriginalSelection = mpEditor->Selection()->SelectedNodeList();
|
||||
|
||||
for (CSelectionIterator It(mpEditor->Selection()); It; ++It)
|
||||
{
|
||||
if (It->NodeType() == eScriptNode)
|
||||
mNodesToClone << *It;
|
||||
}
|
||||
}
|
||||
|
||||
void CCloneSelectionCommand::undo()
|
||||
{
|
||||
QList<CSceneNode*> ClonedNodes = mClonedNodes.DereferenceList();
|
||||
mpEditor->Selection()->Clear();
|
||||
|
||||
foreach (CSceneNode *pNode, ClonedNodes)
|
||||
{
|
||||
CScriptObject *pInst = static_cast<CScriptNode*>(pNode)->Object();
|
||||
|
||||
mpEditor->NotifyNodeAboutToBeDeleted(pNode);
|
||||
mpEditor->Scene()->DeleteNode(pNode);
|
||||
mpEditor->ActiveArea()->DeleteInstance(pInst);
|
||||
mpEditor->NotifyNodeDeleted();
|
||||
}
|
||||
|
||||
mClonedNodes.clear();
|
||||
mpEditor->Selection()->SetSelectedNodes(mOriginalSelection.DereferenceList());
|
||||
}
|
||||
|
||||
void CCloneSelectionCommand::redo()
|
||||
{
|
||||
QList<CSceneNode*> ToClone = mNodesToClone.DereferenceList();
|
||||
QList<CSceneNode*> ClonedNodes;
|
||||
QList<u32> ToCloneInstanceIDs;
|
||||
QList<u32> ClonedInstanceIDs;
|
||||
|
||||
foreach (CSceneNode *pNode, ToClone)
|
||||
{
|
||||
mpEditor->NotifyNodeAboutToBeSpawned();
|
||||
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
|
||||
CScriptObject *pInstance = pScript->Object();
|
||||
|
||||
CScriptObject *pCloneInst = mpEditor->ActiveArea()->SpawnInstance(pInstance->Template(), pInstance->Layer());
|
||||
pCloneInst->Properties()->Copy(pInstance->Properties());
|
||||
pCloneInst->EvaluateProperties();
|
||||
|
||||
CScriptNode *pCloneNode = mpEditor->Scene()->CreateScriptNode(pCloneInst);
|
||||
pCloneNode->SetName(pScript->Name());
|
||||
pCloneNode->SetPosition(pScript->LocalPosition());
|
||||
pCloneNode->SetRotation(pScript->LocalRotation());
|
||||
pCloneNode->SetScale(pScript->LocalScale());
|
||||
pCloneNode->OnLoadFinished();
|
||||
|
||||
ToCloneInstanceIDs << pInstance->InstanceID();
|
||||
ClonedInstanceIDs << pCloneInst->InstanceID();
|
||||
ClonedNodes << pCloneNode;
|
||||
mClonedNodes << pCloneNode;
|
||||
mpEditor->NotifyNodeSpawned(pCloneNode);
|
||||
}
|
||||
|
||||
// Clone outgoing links from source object; incoming ones are discarded
|
||||
QList<CScriptObject*> LinkedInstances;
|
||||
|
||||
for (int iNode = 0; iNode < ClonedNodes.size(); iNode++)
|
||||
{
|
||||
CScriptObject *pSrc = static_cast<CScriptNode*>(ToClone[iNode])->Object();
|
||||
CScriptObject *pClone = static_cast<CScriptNode*>(ClonedNodes[iNode])->Object();
|
||||
|
||||
for (u32 iLink = 0; iLink < pSrc->NumLinks(eOutgoing); iLink++)
|
||||
{
|
||||
CLink *pSrcLink = pSrc->Link(eOutgoing, iLink);
|
||||
|
||||
// If we're cloning the receiver then target the cloned receiver instead of the original one.
|
||||
u32 ReceiverID = pSrcLink->ReceiverID();
|
||||
if (ToCloneInstanceIDs.contains(ReceiverID))
|
||||
ReceiverID = ClonedInstanceIDs[ToCloneInstanceIDs.indexOf(ReceiverID)];
|
||||
|
||||
CLink *pCloneLink = new CLink(pSrcLink->Area(), pSrcLink->State(), pSrcLink->Message(), pClone->InstanceID(), ReceiverID);
|
||||
pCloneLink->Sender()->AddLink(eOutgoing, pCloneLink);
|
||||
pCloneLink->Receiver()->AddLink(eIncoming, pCloneLink);
|
||||
|
||||
if (!LinkedInstances.contains(pCloneLink->Sender()))
|
||||
LinkedInstances << pCloneLink->Sender();
|
||||
if (!LinkedInstances.contains(pCloneLink->Receiver()))
|
||||
LinkedInstances << pCloneLink->Receiver();
|
||||
}
|
||||
}
|
||||
|
||||
mpEditor->OnLinksModified(LinkedInstances);
|
||||
mpEditor->Selection()->SetSelectedNodes(mClonedNodes.DereferenceList());
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef CCLONESELECTIONCOMMAND_H
|
||||
#define CCLONESELECTIONCOMMAND_H
|
||||
|
||||
#include "IUndoCommand.h"
|
||||
#include "ObjReferences.h"
|
||||
#include "Editor/WorldEditor/CWorldEditor.h"
|
||||
|
||||
class CCloneSelectionCommand : public IUndoCommand
|
||||
{
|
||||
CWorldEditor *mpEditor;
|
||||
CNodePtrList mOriginalSelection;
|
||||
CNodePtrList mNodesToClone;
|
||||
CNodePtrList mClonedNodes;
|
||||
|
||||
public:
|
||||
CCloneSelectionCommand(INodeEditor *pEditor);
|
||||
void undo();
|
||||
void redo();
|
||||
bool AffectsCleanState() const { return true; }
|
||||
};
|
||||
|
||||
#endif // CCLONESELECTIONCOMMAND_H
|
|
@ -2,6 +2,7 @@
|
|||
#define UNDOCOMMANDS
|
||||
|
||||
#include "CCreateInstanceCommand.h"
|
||||
#include "CCloneSelectionCommand.h"
|
||||
|
||||
#include "CTranslateNodeCommand.h"
|
||||
#include "CRotateNodeCommand.h"
|
||||
|
|
Loading…
Reference in New Issue