Added support for script instance cloning

This commit is contained in:
parax0 2016-03-19 20:30:42 -06:00
parent 3d6993a1ec
commit 99a64342e9
10 changed files with 186 additions and 36 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,6 +99,8 @@ void INodeEditor::EndGizmoTransform()
foreach (QAction *pAction, mGizmoActions)
pAction->setEnabled(true);
if (mGizmo.HasTransformed())
{
if (mGizmo.Mode() == CGizmo::eTranslate)
mUndoStack.push(CTranslateNodeCommand::End());
else if (mGizmo.Mode() == CGizmo::eRotate)
@ -105,6 +109,12 @@ void INodeEditor::EndGizmoTransform()
mUndoStack.push(CScaleNodeCommand::End());
}
if (mCloneState == eCloning)
mUndoStack.endMacro();
mCloneState = eNotCloning;
}
ETransformSpace INodeEditor::CurrentTransformSpace()
{
switch (mGizmo.Mode())
@ -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:

View File

@ -34,6 +34,7 @@ protected:
bool mGizmoTransforming;
ETransformSpace mTranslateSpace;
ETransformSpace mRotateSpace;
enum { eNotCloning, eReadyToClone, eCloning } mCloneState;
// Gizmo widgets
QActionGroup *mpGizmoGroup;

View File

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

View File

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

View File

@ -2,6 +2,7 @@
#define UNDOCOMMANDS
#include "CCreateInstanceCommand.h"
#include "CCloneSelectionCommand.h"
#include "CTranslateNodeCommand.h"
#include "CRotateNodeCommand.h"