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/Code/Editor/TrackView/TrackViewNode.cpp

711 lines
21 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 "EditorDefs.h"
#include "TrackViewNode.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h>
// Editor
#include "TrackView/TrackViewTrack.h"
#include "TrackView/TrackViewSequence.h"
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyConstHandle::GetKey(IKey* pKey) const
{
m_pTrack->GetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
float CTrackViewKeyConstHandle::GetTime() const
{
return m_pTrack->GetKeyTime(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::SetKey(IKey* pKey)
{
assert(m_bIsValid);
m_pTrack->SetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::GetKey(IKey* pKey) const
{
assert(m_bIsValid);
m_pTrack->GetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::Select(bool bSelect)
{
assert(m_bIsValid);
m_pTrack->SelectKey(m_keyIndex, bSelect);
}
////////////////////////////////////////////////////////////////////////////
bool CTrackViewKeyHandle::IsSelected() const
{
assert(m_bIsValid);
return m_pTrack->IsKeySelected(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::SetTime(float time, bool notifyListeners)
{
AZ_Assert(m_bIsValid, "Expected a valid key handle.");
// Flag the current key, because the key handle may become invalid
// after the time is set and it is potentially sorted into a different
// index.
m_pTrack->SetSortMarkerKey(m_keyIndex, true);
// set the new time, this may cause a sort that reorders the keys, making
// m_keyIndex incorrect.
m_pTrack->SetKeyTime(m_keyIndex, time, notifyListeners);
// If the key at this index changed because of the key sort by time.
// We need to search through the keys now and find the marker.
if (!m_pTrack->IsSortMarkerKey(m_keyIndex))
{
CTrackViewKeyBundle allKeys = m_pTrack->GetAllKeys();
for (unsigned int x = 0; x < allKeys.GetKeyCount(); x++)
{
unsigned int curIndex = allKeys.GetKey(x).GetIndex();
if (m_pTrack->IsSortMarkerKey(curIndex))
{
m_keyIndex = curIndex;
break;
}
}
}
// clear the sort marker
m_pTrack->SetSortMarkerKey(m_keyIndex, false);
}
////////////////////////////////////////////////////////////////////////////
float CTrackViewKeyHandle::GetTime() const
{
assert(m_bIsValid);
return m_pTrack->GetKeyTime(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
float CTrackViewKeyHandle::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* CTrackViewKeyHandle::GetDescription() const
{
assert(m_bIsValid);
const char* desc = "";
float duration = 0;
m_pTrack->m_pAnimTrack->GetKeyInfo(m_keyIndex, desc, duration);
return desc;
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::Offset(float offset, bool notifyListeners)
{
AZ_Assert(m_bIsValid, "Expected key handle to be in a valid state.");
float newTime = m_pTrack->GetKeyTime(m_keyIndex) + offset;
m_pTrack->SetKeyTime(m_keyIndex, newTime, notifyListeners);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::Delete()
{
assert(m_bIsValid);
m_pTrack->RemoveKey(m_keyIndex);
m_bIsValid = false;
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::Clone()
{
assert(m_bIsValid);
unsigned int newKeyIndex = m_pTrack->CloneKey(m_keyIndex);
return CTrackViewKeyHandle(m_pTrack, newKeyIndex);
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetNextKey()
{
return m_pTrack->GetNextKey(GetTime());
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetPrevKey()
{
return m_pTrack->GetPrevKey(GetTime());
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetAboveKey() const
{
// Search for track above that has keys
for (CTrackViewNode* pCurrentNode = m_pTrack->GetAboveNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetAboveNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Track)
{
CTrackViewTrack* pCurrentTrack = static_cast<CTrackViewTrack*>(pCurrentNode);
const unsigned int keyCount = pCurrentTrack->GetKeyCount();
if (keyCount > 0)
{
// Return key with nearest time to this key
return pCurrentTrack->GetNearestKeyByTime(GetTime());
}
}
}
return CTrackViewKeyHandle();
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetBelowKey() const
{
// Search for track below that has keys
for (CTrackViewNode* pCurrentNode = m_pTrack->GetBelowNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetBelowNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Track)
{
CTrackViewTrack* pCurrentTrack = static_cast<CTrackViewTrack*>(pCurrentNode);
const unsigned int keyCount = pCurrentTrack->GetKeyCount();
if (keyCount > 0)
{
// Return key with nearest time to this key
return pCurrentTrack->GetNearestKeyByTime(GetTime());
}
}
}
return CTrackViewKeyHandle();
}
////////////////////////////////////////////////////////////////////////////
bool CTrackViewKeyHandle::operator==(const CTrackViewKeyHandle& keyHandle) const
{
return m_pTrack == keyHandle.m_pTrack && m_keyIndex == keyHandle.m_keyIndex;
}
////////////////////////////////////////////////////////////////////////////
bool CTrackViewKeyHandle::operator!=(const CTrackViewKeyHandle& keyHandle) const
{
return !(*this == keyHandle);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyBundle::AppendKey(const CTrackViewKeyHandle& keyHandle)
{
// Check if newly added key has different type than existing ones
if (m_bAllOfSameType && m_keys.size() > 0)
{
const CTrackViewKeyHandle& lastKey = m_keys.back();
const CTrackViewTrack* pMyTrack = keyHandle.GetTrack();
const CTrackViewTrack* pOtherTrack = lastKey.GetTrack();
// Check if keys are from sub tracks, always compare types of parent track
if (pMyTrack->IsSubTrack())
{
pMyTrack = static_cast<const CTrackViewTrack*>(pMyTrack->GetParentNode());
}
if (pOtherTrack->IsSubTrack())
{
pOtherTrack = static_cast<const CTrackViewTrack*>(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 CTrackViewKeyBundle::AppendKeyBundle(const CTrackViewKeyBundle& bundle)
{
for (auto iter = bundle.m_keys.begin(); iter != bundle.m_keys.end(); ++iter)
{
AppendKey(*iter);
}
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyBundle::SelectKeys(const bool bSelected)
{
const unsigned int numKeys = GetKeyCount();
for (unsigned int i = 0; i < numKeys; ++i)
{
GetKey(i).Select(bSelected);
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyBundle::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
CTrackViewNode* pFirstParent = m_keys[0].GetTrack()->GetParentNode();
const float firstTime = m_keys[0].GetTime();
// Parent must be a track
if (pFirstParent->GetNodeType() != eTVNT_Track)
{
return CTrackViewKeyHandle();
}
// 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 CTrackViewKeyHandle();
}
}
return static_cast<CTrackViewTrack*>(pFirstParent)->GetKeyByTime(firstTime);
}
return CTrackViewKeyHandle();
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode::CTrackViewNode(CTrackViewNode* pParent)
: m_pParentNode(pParent)
, m_bSelected(false)
, m_bHidden(false)
{
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewNode::HasObsoleteTrack() const
{
return HasObsoleteTrackRec(this);
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewNode::HasObsoleteTrackRec(const CTrackViewNode* pCurrentNode) const
{
if (pCurrentNode->GetNodeType() == eTVNT_Track)
{
const CTrackViewTrack* pTrack = static_cast<const CTrackViewTrack*>(pCurrentNode);
EAnimCurveType trackType = pTrack->GetCurveType();
if (trackType == eAnimCurveType_TCBFloat || trackType == eAnimCurveType_TCBQuat || trackType == eAnimCurveType_TCBVector)
{
return true;
}
}
for (unsigned int i = 0; i < pCurrentNode->GetChildCount(); ++i)
{
CTrackViewNode* pNode = pCurrentNode->GetChild(i);
if (HasObsoleteTrackRec(pNode))
{
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::ClearSelection()
{
CTrackViewSequenceNotificationContext context(GetSequence());
SetSelected(false);
const unsigned int numChilds = GetChildCount();
for (unsigned int childIndex = 0; childIndex < numChilds; ++childIndex)
{
GetChild(childIndex)->ClearSelection();
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetAboveNode() const
{
CTrackViewNode* pParent = GetParentNode();
if (!pParent)
{
// The root does not have an above node
return nullptr;
}
CTrackViewNode* pPrevSibling = GetPrevSibling();
if (!pPrevSibling)
{
// First sibling -> parent is above node
return pParent;
}
// Find last node in sibling tree
CTrackViewNode* pCurrentNode = pPrevSibling;
while (pCurrentNode && pCurrentNode->GetChildCount() > 0 && pCurrentNode->GetExpanded())
{
pCurrentNode = pCurrentNode->GetChild(pCurrentNode->GetChildCount() - 1);
}
return pCurrentNode;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetBelowNode() const
{
const unsigned int childCount = GetChildCount();
if (childCount > 0 && GetExpanded())
{
return GetChild(0);
}
CTrackViewNode* pParent = GetParentNode();
if (!pParent)
{
// Root without children
return nullptr;
}
// If there is a next sibling return it
CTrackViewNode* pNextSibling = GetNextSibling();
if (pNextSibling)
{
return pNextSibling;
}
// Otherwise we need to go up the tree and check
// the parent nodes for next siblings
CTrackViewNode* pCurrentNode = pParent;
while (pCurrentNode)
{
CTrackViewNode* pNextParentSibling = pCurrentNode->GetNextSibling();
if (pNextParentSibling)
{
return pNextParentSibling;
}
pCurrentNode = pCurrentNode->GetParentNode();
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetPrevSibling() const
{
CTrackViewNode* 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)
{
CTrackViewNode* pSibling = pParent->GetChild(i);
if (pSibling == this)
{
return pParent->GetChild(i - 1);
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetNextSibling() const
{
CTrackViewNode* 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)
{
CTrackViewNode* pSibling = pParent->GetChild(i);
if (pSibling == this)
{
return pParent->GetChild(i + 1);
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::SetSelected(bool bSelected)
{
if (bSelected != m_bSelected)
{
m_bSelected = bSelected;
if (m_bSelected)
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Selected);
}
else
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Deselected);
}
GetSequence()->OnNodeSelectionChanged();
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewSequence* CTrackViewNode::GetSequence()
{
for (CTrackViewNode* pCurrentNode = this; pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Sequence)
{
return static_cast<CTrackViewSequence*>(pCurrentNode);
}
}
// Every node belongs to a sequence
assert(false);
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
const CTrackViewSequence* CTrackViewNode::GetSequenceConst() const
{
for (const CTrackViewNode* pCurrentNode = this; pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Sequence)
{
return static_cast<const CTrackViewSequence*>(pCurrentNode);
}
}
AZ_Assert(false, "Every node belongs to a sequence");
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::AddNode(CTrackViewNode* pNode)
{
assert (pNode->GetNodeType() != eTVNT_Sequence);
m_childNodes.push_back(std::unique_ptr<CTrackViewNode>(pNode));
SortNodes();
pNode->m_pParentNode = this;
GetSequence()->OnNodeChanged(pNode, ITrackViewSequenceListener::eNodeChangeType_Added);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::SortNodes()
{
// Sort with operator<
std::stable_sort(m_childNodes.begin(), m_childNodes.end(),
[&](const std::unique_ptr<CTrackViewNode>& a, const std::unique_ptr<CTrackViewNode>& b) -> bool
{
const CTrackViewNode* pA = a.get();
const CTrackViewNode* pB = b.get();
return *pA < *pB;
}
);
}
namespace
{
static int GetNodeOrder(AnimNodeType nodeType)
{
AZ_Assert(nodeType < AnimNodeType::Num, "Expected nodeType to be less than AnimNodeType::Num");
// note: this array gets over-allocated and is sparsely populated because the eAnimNodeType enums are not sequential in IMovieSystem.h
// I wonder if the original authors intended this? Not a big deal, just some trivial memory wastage.
static int nodeOrder[static_cast<int>(AnimNodeType::Num)];
nodeOrder[static_cast<int>(AnimNodeType::Invalid)] = 0;
nodeOrder[static_cast<int>(AnimNodeType::Director)] = 1;
nodeOrder[static_cast<int>(AnimNodeType::Alembic)] = 4;
nodeOrder[static_cast<int>(AnimNodeType::CVar)] = 6;
nodeOrder[static_cast<int>(AnimNodeType::ScriptVar)] = 7;
nodeOrder[static_cast<int>(AnimNodeType::Material)] = 8;
nodeOrder[static_cast<int>(AnimNodeType::Event)] = 9;
nodeOrder[static_cast<int>(AnimNodeType::Layer)] = 10;
nodeOrder[static_cast<int>(AnimNodeType::Comment)] = 11;
nodeOrder[static_cast<int>(AnimNodeType::RadialBlur)] = 12;
nodeOrder[static_cast<int>(AnimNodeType::ColorCorrection)] = 13;
nodeOrder[static_cast<int>(AnimNodeType::DepthOfField)] = 14;
nodeOrder[static_cast<int>(AnimNodeType::ScreenFader)] = 15;
nodeOrder[static_cast<int>(AnimNodeType::Light)] = 16;
nodeOrder[static_cast<int>(AnimNodeType::ShadowSetup)] = 17;
nodeOrder[static_cast<int>(AnimNodeType::Environment)] = 18;
nodeOrder[static_cast<int>(AnimNodeType::Group)] = 19;
return nodeOrder[static_cast<int>(nodeType)];
}
}
bool CTrackViewNode::operator<(const CTrackViewNode& 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 eTVNT_AnimNode:
{
const CTrackViewAnimNode& thisAnimNode = static_cast<const CTrackViewAnimNode&>(*this);
const CTrackViewAnimNode& otherAnimNode = static_cast<const CTrackViewAnimNode&>(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 eTVNT_Track:
const CTrackViewTrack& thisTrack = static_cast<const CTrackViewTrack&>(*this);
const CTrackViewTrack& otherTrack = static_cast<const CTrackViewTrack&>(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 CTrackViewNode::SetHidden(bool bHidden)
{
bool bWasHidden = m_bHidden;
m_bHidden = bHidden;
if (bHidden && !bWasHidden)
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Hidden);
}
else if (!bHidden && bWasHidden)
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Unhidden);
}
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewNode::IsHidden() const
{
return m_bHidden;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetFirstSelectedNode()
{
if (IsSelected())
{
return this;
}
const unsigned int numChilds = GetChildCount();
for (unsigned int childIndex = 0; childIndex < numChilds; ++childIndex)
{
CTrackViewNode* pSelectedNode = GetChild(childIndex)->GetFirstSelectedNode();
if (pSelectedNode)
{
return pSelectedNode;
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewAnimNode* CTrackViewNode::GetDirector()
{
for (CTrackViewNode* pCurrentNode = GetParentNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_AnimNode)
{
CTrackViewAnimNode* pParentAnimNode = static_cast<CTrackViewAnimNode*>(pCurrentNode);
if (pParentAnimNode->GetType() == AnimNodeType::Director)
{
return pParentAnimNode;
}
}
else if (pCurrentNode->GetNodeType() == eTVNT_Sequence)
{
return static_cast<CTrackViewAnimNode*>(pCurrentNode);
}
}
return nullptr;
}