You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/LyShine/Code/Editor/Animation/UiAnimViewNode.cpp

687 lines
20 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "UiAnimViewAnimNode.h"
#include "UiAnimViewTrack.h"
#include "UiAnimViewSequence.h"
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyConstHandle::GetKey(IKey* pKey) const
{
assert(m_bIsValid);
m_pTrack->GetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
float CUiAnimViewKeyConstHandle::GetTime() const
{
assert(m_bIsValid);
return m_pTrack->GetKeyTime(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyHandle::SetKey(IKey* pKey)
{
assert(m_bIsValid);
m_pTrack->SetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyHandle::GetKey(IKey* pKey) const
{
assert(m_bIsValid);
m_pTrack->GetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyHandle::Select(bool bSelect)
{
assert(m_bIsValid);
m_pTrack->SelectKey(m_keyIndex, bSelect);
}
////////////////////////////////////////////////////////////////////////////
bool CUiAnimViewKeyHandle::IsSelected() const
{
assert(m_bIsValid);
return m_pTrack->IsKeySelected(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyHandle::SetTime(float time)
{
assert(m_bIsValid);
m_pTrack->SetKeyTime(m_keyIndex, time);
}
////////////////////////////////////////////////////////////////////////////
float CUiAnimViewKeyHandle::GetTime() const
{
assert(m_bIsValid);
return m_pTrack->GetKeyTime(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
float CUiAnimViewKeyHandle::GetDuration() const
{
assert(m_bIsValid);
const char* desc = nullptr;
float duration = 0;
m_pTrack->m_pAnimTrack->GetKeyInfo(m_keyIndex, desc, duration);
return duration;
}
////////////////////////////////////////////////////////////////////////////
const char* CUiAnimViewKeyHandle::GetDescription() const
{
assert(m_bIsValid);
const char* desc = "";
float duration = 0;
m_pTrack->m_pAnimTrack->GetKeyInfo(m_keyIndex, desc, duration);
return desc;
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyHandle::Offset(float offset)
{
assert(m_bIsValid);
float newTime = m_pTrack->GetKeyTime(m_keyIndex) + offset;
m_pTrack->SetKeyTime(m_keyIndex, newTime);
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyHandle::Delete()
{
assert(m_bIsValid);
m_pTrack->RemoveKey(m_keyIndex);
m_bIsValid = false;
}
////////////////////////////////////////////////////////////////////////////
CUiAnimViewKeyHandle CUiAnimViewKeyHandle::Clone()
{
assert(m_bIsValid);
unsigned int newKeyIndex = m_pTrack->CloneKey(m_keyIndex);
return CUiAnimViewKeyHandle(m_pTrack, newKeyIndex);
}
////////////////////////////////////////////////////////////////////////////
CUiAnimViewKeyHandle CUiAnimViewKeyHandle::GetNextKey()
{
return m_pTrack->GetNextKey(GetTime());
}
////////////////////////////////////////////////////////////////////////////
CUiAnimViewKeyHandle CUiAnimViewKeyHandle::GetPrevKey()
{
return m_pTrack->GetPrevKey(GetTime());
}
////////////////////////////////////////////////////////////////////////////
CUiAnimViewKeyHandle CUiAnimViewKeyHandle::GetAboveKey() const
{
// Search for track above that has keys
for (CUiAnimViewNode* pCurrentNode = m_pTrack->GetAboveNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetAboveNode())
{
if (pCurrentNode->GetNodeType() == eUiAVNT_Track)
{
CUiAnimViewTrack* pCurrentTrack = static_cast<CUiAnimViewTrack*>(pCurrentNode);
const unsigned int keyCount = pCurrentTrack->GetKeyCount();
if (keyCount > 0)
{
// Return key with nearest time to this key
return pCurrentTrack->GetNearestKeyByTime(GetTime());
}
}
}
return CUiAnimViewKeyHandle();
}
////////////////////////////////////////////////////////////////////////////
CUiAnimViewKeyHandle CUiAnimViewKeyHandle::GetBelowKey() const
{
// Search for track below that has keys
for (CUiAnimViewNode* pCurrentNode = m_pTrack->GetBelowNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetBelowNode())
{
if (pCurrentNode->GetNodeType() == eUiAVNT_Track)
{
CUiAnimViewTrack* pCurrentTrack = static_cast<CUiAnimViewTrack*>(pCurrentNode);
const unsigned int keyCount = pCurrentTrack->GetKeyCount();
if (keyCount > 0)
{
// Return key with nearest time to this key
return pCurrentTrack->GetNearestKeyByTime(GetTime());
}
}
}
return CUiAnimViewKeyHandle();
}
////////////////////////////////////////////////////////////////////////////
bool CUiAnimViewKeyHandle::operator==(const CUiAnimViewKeyHandle& keyHandle) const
{
return m_pTrack == keyHandle.m_pTrack && m_keyIndex == keyHandle.m_keyIndex;
}
////////////////////////////////////////////////////////////////////////////
bool CUiAnimViewKeyHandle::operator!=(const CUiAnimViewKeyHandle& keyHandle) const
{
return !(*this == keyHandle);
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyBundle::AppendKey(const CUiAnimViewKeyHandle& keyHandle)
{
// Check if newly added key has different type than existing ones
if (m_bAllOfSameType && m_keys.size() > 0)
{
const CUiAnimViewKeyHandle& lastKey = m_keys.back();
const CUiAnimViewTrack* pMyTrack = keyHandle.GetTrack();
const CUiAnimViewTrack* pOtherTrack = lastKey.GetTrack();
// Check if keys are from sub tracks, always compare types of parent track
if (pMyTrack->IsSubTrack())
{
pMyTrack = static_cast<const CUiAnimViewTrack*>(pMyTrack->GetParentNode());
}
if (pOtherTrack->IsSubTrack())
{
pOtherTrack = static_cast<const CUiAnimViewTrack*>(pOtherTrack->GetParentNode());
}
// Do comparison
if (pMyTrack->GetParameterType() != pOtherTrack->GetParameterType()
|| pMyTrack->GetCurveType() != pOtherTrack->GetCurveType()
|| pMyTrack->GetValueType() != pOtherTrack->GetValueType())
{
m_bAllOfSameType = false;
}
}
m_keys.push_back(keyHandle);
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyBundle::AppendKeyBundle(const CUiAnimViewKeyBundle& bundle)
{
for (auto iter = bundle.m_keys.begin(); iter != bundle.m_keys.end(); ++iter)
{
AppendKey(*iter);
}
}
////////////////////////////////////////////////////////////////////////////
void CUiAnimViewKeyBundle::SelectKeys(const bool bSelected)
{
const unsigned int numKeys = GetKeyCount();
for (unsigned int i = 0; i < numKeys; ++i)
{
GetKey(i).Select(bSelected);
}
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewKeyHandle CUiAnimViewKeyBundle::GetSingleSelectedKey()
{
const unsigned int keyCount = GetKeyCount();
if (keyCount == 1)
{
return m_keys[0];
}
else if (keyCount > 1 && keyCount <= 4)
{
// All keys must have same time & same parent track
CUiAnimViewNode* pFirstParent = m_keys[0].GetTrack()->GetParentNode();
const float firstTime = m_keys[0].GetTime();
// Parent must be a track
if (pFirstParent->GetNodeType() != eUiAVNT_Track)
{
return CUiAnimViewKeyHandle();
}
// Check other keys for equality
for (unsigned int i = 0; i < keyCount; ++i)
{
if (m_keys[i].GetTrack()->GetParentNode() != pFirstParent || m_keys[i].GetTime() != firstTime)
{
return CUiAnimViewKeyHandle();
}
}
return static_cast<CUiAnimViewTrack*>(pFirstParent)->GetKeyByTime(firstTime);
}
return CUiAnimViewKeyHandle();
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewNode::CUiAnimViewNode(CUiAnimViewNode* pParent)
: m_pParentNode(pParent)
, m_bSelected(false)
, m_bExpanded(false)
, m_bHidden(false)
{
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimViewNode::HasObsoleteTrack() const
{
return HasObsoleteTrackRec(this);
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimViewNode::HasObsoleteTrackRec(const CUiAnimViewNode* pCurrentNode) const
{
if (pCurrentNode->GetNodeType() == eUiAVNT_Track)
{
const CUiAnimViewTrack* pTrack = static_cast<const CUiAnimViewTrack*>(pCurrentNode);
EUiAnimCurveType trackType = pTrack->GetCurveType();
if (trackType == eUiAnimCurveType_TCBFloat || trackType == eUiAnimCurveType_TCBQuat || trackType == eUiAnimCurveType_TCBVector)
{
return true;
}
}
for (unsigned int i = 0; i < pCurrentNode->GetChildCount(); ++i)
{
CUiAnimViewNode* pNode = pCurrentNode->GetChild(i);
if (HasObsoleteTrackRec(pNode))
{
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimViewNode::ClearSelection()
{
CUiAnimViewSequenceNotificationContext context(GetSequence());
SetSelected(false);
const unsigned int numChilds = GetChildCount();
for (unsigned int childIndex = 0; childIndex < numChilds; ++childIndex)
{
GetChild(childIndex)->ClearSelection();
}
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewNode* CUiAnimViewNode::GetAboveNode() const
{
CUiAnimViewNode* pParent = GetParentNode();
if (!pParent)
{
// The root does not have an above node
return nullptr;
}
CUiAnimViewNode* pPrevSibling = GetPrevSibling();
if (!pPrevSibling)
{
// First sibling -> parent is above node
return pParent;
}
// Find last node in sibling tree
CUiAnimViewNode* pCurrentNode = pPrevSibling;
while (pCurrentNode && pCurrentNode->GetChildCount() > 0 && pCurrentNode->IsExpanded())
{
pCurrentNode = pCurrentNode->GetChild(pCurrentNode->GetChildCount() - 1);
}
return pCurrentNode;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewNode* CUiAnimViewNode::GetBelowNode() const
{
const unsigned int childCount = GetChildCount();
if (childCount > 0 && IsExpanded())
{
return GetChild(0);
}
CUiAnimViewNode* pParent = GetParentNode();
if (!pParent)
{
// Root without children
return nullptr;
}
// If there is a next sibling return it
CUiAnimViewNode* pNextSibling = GetNextSibling();
if (pNextSibling)
{
return pNextSibling;
}
// Otherwise we need to go up the tree and check
// the parent nodes for next siblings
CUiAnimViewNode* pCurrentNode = pParent;
while (pCurrentNode)
{
CUiAnimViewNode* pNextParentSibling = pCurrentNode->GetNextSibling();
if (pNextParentSibling)
{
return pNextParentSibling;
}
pCurrentNode = pCurrentNode->GetParentNode();
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewNode* CUiAnimViewNode::GetPrevSibling() const
{
CUiAnimViewNode* pParent = GetParentNode();
if (!pParent)
{
// The root does not have siblings
return nullptr;
}
// Search for prev sibling
unsigned int siblingCount = pParent->GetChildCount();
assert(siblingCount > 0);
for (unsigned int i = 1; i < siblingCount; ++i)
{
CUiAnimViewNode* pSibling = pParent->GetChild(i);
if (pSibling == this)
{
return pParent->GetChild(i - 1);
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewNode* CUiAnimViewNode::GetNextSibling() const
{
CUiAnimViewNode* pParent = GetParentNode();
if (!pParent)
{
// The root does not have siblings
return nullptr;
}
// Search for next sibling
unsigned int siblingCount = pParent->GetChildCount();
assert(siblingCount > 0);
for (unsigned int i = 0; i < siblingCount - 1; ++i)
{
CUiAnimViewNode* pSibling = pParent->GetChild(i);
if (pSibling == this)
{
return pParent->GetChild(i + 1);
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimViewNode::SetSelected(bool bSelected)
{
if (bSelected != m_bSelected)
{
m_bSelected = bSelected;
if (m_bSelected)
{
GetSequence()->OnNodeChanged(this, IUiAnimViewSequenceListener::eNodeChangeType_Selected);
}
else
{
GetSequence()->OnNodeChanged(this, IUiAnimViewSequenceListener::eNodeChangeType_Deselected);
}
GetSequence()->OnNodeSelectionChanged();
}
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewSequence* CUiAnimViewNode::GetSequence()
{
for (CUiAnimViewNode* pCurrentNode = this; pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eUiAVNT_Sequence)
{
return static_cast<CUiAnimViewSequence*>(pCurrentNode);
}
}
// Every node belongs to a sequence
assert(false);
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimViewNode::SetExpanded(bool bExpanded)
{
if (bExpanded != m_bExpanded)
{
m_bExpanded = bExpanded;
if (bExpanded)
{
GetSequence()->OnNodeChanged(this, IUiAnimViewSequenceListener::eNodeChangeType_Expanded);
}
else
{
GetSequence()->OnNodeChanged(this, IUiAnimViewSequenceListener::eNodeChangeType_Collapsed);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimViewNode::AddNode(CUiAnimViewNode* pNode)
{
assert (pNode->GetNodeType() != eUiAVNT_Sequence);
m_childNodes.push_back(std::unique_ptr<CUiAnimViewNode>(pNode));
SortNodes();
pNode->m_pParentNode = this;
GetSequence()->OnNodeChanged(pNode, IUiAnimViewSequenceListener::eNodeChangeType_Added);
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimViewNode::SortNodes()
{
// Sort with operator<
std::stable_sort(m_childNodes.begin(), m_childNodes.end(),
[&](const std::unique_ptr<CUiAnimViewNode>& a, const std::unique_ptr<CUiAnimViewNode>& b) -> bool
{
const CUiAnimViewNode* pA = a.get();
const CUiAnimViewNode* pB = b.get();
return *pA < *pB;
}
);
}
namespace
{
static int GetNodeOrder(EUiAnimNodeType nodeType)
{
assert(nodeType < eUiAnimNodeType_Num);
static int nodeOrder[eUiAnimNodeType_Num];
nodeOrder[eUiAnimNodeType_Invalid] = 0;
nodeOrder[eUiAnimNodeType_Director] = 1;
nodeOrder[eUiAnimNodeType_Camera] = 2;
nodeOrder[eUiAnimNodeType_Entity] = 3;
nodeOrder[eUiAnimNodeType_Alembic] = 4;
nodeOrder[eUiAnimNodeType_GeomCache] = 5;
nodeOrder[eUiAnimNodeType_CVar] = 6;
nodeOrder[eUiAnimNodeType_ScriptVar] = 7;
nodeOrder[eUiAnimNodeType_Material] = 8;
nodeOrder[eUiAnimNodeType_Event] = 9;
nodeOrder[eUiAnimNodeType_Layer] = 10;
nodeOrder[eUiAnimNodeType_Comment] = 11;
nodeOrder[eUiAnimNodeType_RadialBlur] = 12;
nodeOrder[eUiAnimNodeType_ColorCorrection] = 13;
nodeOrder[eUiAnimNodeType_DepthOfField] = 14;
nodeOrder[eUiAnimNodeType_ScreenFader] = 15;
nodeOrder[eUiAnimNodeType_Light] = 16;
nodeOrder[eUiAnimNodeType_HDRSetup] = 17;
nodeOrder[eUiAnimNodeType_ShadowSetup] = 18;
nodeOrder[eUiAnimNodeType_Environment] = 19;
nodeOrder[eUiAnimNodeType_ScreenDropsSetup] = 20;
nodeOrder[eUiAnimNodeType_Group] = 21;
return nodeOrder[nodeType];
}
}
bool CUiAnimViewNode::operator<(const CUiAnimViewNode& otherNode) const
{
// Order nodes before tracks
if (GetNodeType() < otherNode.GetNodeType())
{
return true;
}
else if (GetNodeType() > otherNode.GetNodeType())
{
return false;
}
// Same node type
switch (GetNodeType())
{
case eUiAVNT_AnimNode:
{
const CUiAnimViewAnimNode& thisAnimNode = static_cast<const CUiAnimViewAnimNode&>(*this);
const CUiAnimViewAnimNode& otherAnimNode = static_cast<const CUiAnimViewAnimNode&>(otherNode);
const int thisTypeOrder = GetNodeOrder(thisAnimNode.GetType());
const int otherTypeOrder = GetNodeOrder(otherAnimNode.GetType());
if (thisTypeOrder == otherTypeOrder)
{
// Same node type, sort by name
return thisAnimNode.GetName() < otherAnimNode.GetName();
}
return thisTypeOrder < otherTypeOrder;
}
case eUiAVNT_Track:
const CUiAnimViewTrack& thisTrack = static_cast<const CUiAnimViewTrack&>(*this);
const CUiAnimViewTrack& otherTrack = static_cast<const CUiAnimViewTrack&>(otherNode);
if (thisTrack.GetParameterType() == otherTrack.GetParameterType())
{
// Same parameter type, sort by name
return thisTrack.GetName() < otherTrack.GetName();
}
return thisTrack.GetParameterType() < otherTrack.GetParameterType();
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimViewNode::SetHidden(bool bHidden)
{
bool bWasHidden = m_bHidden;
m_bHidden = bHidden;
if (bHidden && !bWasHidden)
{
GetSequence()->OnNodeChanged(this, IUiAnimViewSequenceListener::eNodeChangeType_Hidden);
}
else if (!bHidden && bWasHidden)
{
GetSequence()->OnNodeChanged(this, IUiAnimViewSequenceListener::eNodeChangeType_Unhidden);
}
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimViewNode::IsHidden() const
{
return m_bHidden;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewNode* CUiAnimViewNode::GetFirstSelectedNode()
{
if (IsSelected())
{
return this;
}
const unsigned int numChilds = GetChildCount();
for (unsigned int childIndex = 0; childIndex < numChilds; ++childIndex)
{
CUiAnimViewNode* pSelectedNode = GetChild(childIndex)->GetFirstSelectedNode();
if (pSelectedNode)
{
return pSelectedNode;
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewAnimNode* CUiAnimViewNode::GetDirector()
{
for (CUiAnimViewNode* pCurrentNode = GetParentNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eUiAVNT_AnimNode)
{
CUiAnimViewAnimNode* pParentAnimNode = static_cast<CUiAnimViewAnimNode*>(pCurrentNode);
if (pParentAnimNode->GetType() == eUiAnimNodeType_Director)
{
return pParentAnimNode;
}
}
else if (pCurrentNode->GetNodeType() == eUiAVNT_Sequence)
{
return static_cast<CUiAnimViewAnimNode*>(pCurrentNode);
}
}
return nullptr;
}