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/Source/Animation/AnimSequence.cpp

1233 lines
34 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
*
*/
// Description : Implementation of IAnimSequence interface.
#include "AnimSequence.h"
#include "StlUtils.h"
#include "EventNode.h"
#include "AzEntityNode.h"
#include "UiAnimSerialize.h"
//////////////////////////////////////////////////////////////////////////
CUiAnimSequence::CUiAnimSequence()
: CUiAnimSequence(nullptr, 0)
{
}
//////////////////////////////////////////////////////////////////////////
CUiAnimSequence::CUiAnimSequence(IUiAnimationSystem* pUiAnimationSystem, uint32 id)
: m_refCount(0)
{
m_nextGenId = 1;
m_pUiAnimationSystem = pUiAnimationSystem;
m_flags = 0;
m_pParentSequence = NULL;
m_timeRange.Set(0, 10);
m_bPaused = false;
m_bActive = false;
m_pOwner = NULL;
m_pActiveDirector = NULL;
m_precached = false;
m_bResetting = false;
m_id = id;
m_time = -FLT_MAX;
m_pEventStrings = aznew CUiAnimStringTable;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimSequence::~CUiAnimSequence()
{
// clear reference to me from all my nodes
for (int i = static_cast<int>(m_nodes.size()); --i >= 0;)
{
if (m_nodes[i])
{
m_nodes[i]->SetSequence(nullptr);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::add_ref()
{
++m_refCount;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::release()
{
if (--m_refCount <= 0)
{
delete this;
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::SetName(const char* name)
{
if (!m_pUiAnimationSystem)
{
return; // should never happen, null pointer guard
}
AZStd::string originalName = GetName();
m_name = name;
m_pUiAnimationSystem->OnSequenceRenamed(originalName.c_str(), m_name.c_str());
if (GetOwner())
{
GetOwner()->OnModified();
}
}
//////////////////////////////////////////////////////////////////////////
const char* CUiAnimSequence::GetName() const
{
return m_name.c_str();
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::SetFlags(int flags)
{
m_flags = flags;
}
//////////////////////////////////////////////////////////////////////////
int CUiAnimSequence::GetFlags() const
{
return m_flags;
}
//////////////////////////////////////////////////////////////////////////
int CUiAnimSequence::GetCutSceneFlags(const bool localFlags) const
{
int currentFlags = m_flags & (eSeqFlags_NoHUD | eSeqFlags_NoPlayer | eSeqFlags_16To9 | eSeqFlags_NoGameSounds | eSeqFlags_NoAbort);
if (m_pParentSequence != NULL)
{
if (localFlags == true)
{
currentFlags &= ~m_pParentSequence->GetCutSceneFlags();
}
else
{
currentFlags |= m_pParentSequence->GetCutSceneFlags();
}
}
return currentFlags;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::SetParentSequence(IUiAnimSequence* pParentSequence)
{
m_pParentSequence = pParentSequence;
}
//////////////////////////////////////////////////////////////////////////
const IUiAnimSequence* CUiAnimSequence::GetParentSequence() const
{
return m_pParentSequence;
}
//////////////////////////////////////////////////////////////////////////
int CUiAnimSequence::GetNodeCount() const
{
return static_cast<int>(m_nodes.size());
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimSequence::GetNode(int index) const
{
assert(index >= 0 && index < (int)m_nodes.size());
return m_nodes[index].get();
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::AddNode(IUiAnimNode* pAnimNode)
{
AZ_Assert(pAnimNode, "Expected valid animNode");
if(!pAnimNode)
{
return false;
}
pAnimNode->SetSequence(this);
pAnimNode->SetTimeRange(m_timeRange);
// Check if this node already in sequence.
bool found = false;
for (int i = 0; i < (int)m_nodes.size(); i++)
{
if (pAnimNode == m_nodes[i].get())
{
found = true;
break;
}
}
if (!found)
{
m_nodes.push_back(AZStd::intrusive_ptr<IUiAnimNode>(pAnimNode));
}
const int nodeId = static_cast<CUiAnimNode*>(pAnimNode)->GetId();
if (nodeId >= static_cast<int>(m_nextGenId))
{
m_nextGenId = nodeId + 1;
}
if (pAnimNode->NeedToRender())
{
AddNodeNeedToRender(pAnimNode);
}
bool bNewDirectorNode = m_pActiveDirector == NULL && pAnimNode->GetType() == eUiAnimNodeType_Director;
if (bNewDirectorNode)
{
m_pActiveDirector = pAnimNode;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimSequence::CreateNodeInternal(EUiAnimNodeType nodeType, uint32 nNodeId)
{
CUiAnimNode* pAnimNode = NULL;
if (nNodeId == -1)
{
nNodeId = m_nextGenId;
}
switch (nodeType)
{
case eUiAnimNodeType_AzEntity:
pAnimNode = aznew CUiAnimAzEntityNode(nNodeId);
break;
case eUiAnimNodeType_Event:
pAnimNode = aznew CUiAnimEventNode(nNodeId);
break;
}
if (pAnimNode)
{
AddNode(pAnimNode);
}
return pAnimNode;
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimSequence::CreateNode(EUiAnimNodeType nodeType)
{
return CreateNodeInternal(nodeType);
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimSequence::CreateNode(XmlNodeRef node)
{
if (!GetUiAnimationSystem())
{
return 0; // should never happen, null pointer guard
}
EUiAnimNodeType type;
static_cast<UiAnimationSystem*>(GetUiAnimationSystem())->SerializeNodeType(type, node, true, IUiAnimSequence::kSequenceVersion, 0);
XmlString name;
if (!node->getAttr("Name", name))
{
return 0;
}
IUiAnimNode* pNewNode = CreateNode(type);
if (!pNewNode)
{
return 0;
}
pNewNode->SetName(name);
pNewNode->Serialize(node, true, true);
CUiAnimNode* newAnimNode = static_cast<CUiAnimNode*>(pNewNode);
// Make sure de-serializing this node didn't just create an id conflict. This can happen sometimes
// when copy/pasting nodes from a different sequence to this one.
for (auto curNode : m_nodes)
{
CUiAnimNode* animNode = static_cast<CUiAnimNode*>(curNode.get());
if (animNode->GetId() == newAnimNode->GetId() && animNode != newAnimNode)
{
// Conflict detected, resolve it by assigning a new id to the new node.
newAnimNode->SetId(m_nextGenId++);
}
}
return pNewNode;
}
//////////////////////////////////////////////////////////////////////////
// Only called from undo/redo
void CUiAnimSequence::RemoveNode(IUiAnimNode* node)
{
assert(node != 0);
static_cast<CUiAnimNode*>(node)->Activate(false);
static_cast<CUiAnimNode*>(node)->OnReset();
for (int i = 0; i < (int)m_nodes.size(); )
{
if (node == m_nodes[i].get())
{
m_nodes.erase(m_nodes.begin() + i);
if (node->NeedToRender())
{
RemoveNodeNeedToRender(node);
}
continue;
}
if (m_nodes[i]->GetParent() == node)
{
m_nodes[i]->SetParent(0);
}
i++;
}
// The removed one was the active director node.
if (m_pActiveDirector == node)
{
// Clear the active one.
m_pActiveDirector = NULL;
// If there is another director node, set it as active.
for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pNode = it->get();
if (pNode->GetType() == eUiAnimNodeType_Director)
{
SetActiveDirector(pNode);
break;
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::RemoveAll()
{
stl::free_container(m_nodes);
stl::free_container(m_events);
stl::free_container(m_nodesNeedToRender);
m_pActiveDirector = NULL;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Reset(bool bSeekToStart)
{
if (GetFlags() & eSeqFlags_LightAnimationSet)
{
return;
}
m_precached = false;
m_bResetting = true;
if (!bSeekToStart)
{
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnReset();
}
m_bResetting = false;
return;
}
bool bWasActive = m_bActive;
if (!bWasActive)
{
Activate();
}
SUiAnimContext ec;
ec.bSingleFrame = true;
ec.bResetting = true;
ec.pSequence = this;
ec.time = m_timeRange.start;
Animate(ec);
if (!bWasActive)
{
Deactivate();
}
else
{
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnReset();
}
}
m_bResetting = false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::ResetHard()
{
if (GetFlags() & eSeqFlags_LightAnimationSet)
{
return;
}
m_bResetting = true;
bool bWasActive = m_bActive;
if (!bWasActive)
{
Activate();
}
SUiAnimContext ec;
ec.bSingleFrame = true;
ec.bResetting = true;
ec.pSequence = this;
ec.time = m_timeRange.start;
Animate(ec);
if (!bWasActive)
{
Deactivate();
}
else
{
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnResetHard();
}
}
m_bResetting = false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Pause()
{
if (GetFlags() & eSeqFlags_LightAnimationSet || m_bPaused)
{
return;
}
m_bPaused = true;
// Detach animation block from all nodes in this sequence.
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnPause();
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Resume()
{
if (GetFlags() & eSeqFlags_LightAnimationSet)
{
return;
}
if (m_bPaused)
{
m_bPaused = false;
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnResume();
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::OnLoop()
{
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnLoop();
}
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::IsPaused() const
{
return m_bPaused;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::OnStart()
{
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnStart();
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::OnStop()
{
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->OnStop();
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::StillUpdate()
{
if (GetFlags() & eSeqFlags_LightAnimationSet)
{
return;
}
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
pAnimNode->StillUpdate();
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Animate(const SUiAnimContext& ec)
{
assert(m_bActive);
if (GetFlags() & eSeqFlags_LightAnimationSet)
{
return;
}
SUiAnimContext animContext = ec;
animContext.pSequence = this;
m_time = animContext.time;
// Evaluate all animation nodes in sequence.
// The director first.
if (m_pActiveDirector)
{
m_pActiveDirector->Animate(animContext);
}
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
// Make sure correct animation block is binded to node.
IUiAnimNode* pAnimNode = it->get();
// All other (inactive) director nodes are skipped.
if (pAnimNode->GetType() == eUiAnimNodeType_Director)
{
continue;
}
// If this is a descendant of a director node and that director is currently not active, skip this one.
IUiAnimNode* pParentDirector = pAnimNode->HasDirectorAsParent();
if (pParentDirector && pParentDirector != m_pActiveDirector)
{
continue;
}
if (pAnimNode->GetFlags() & eUiAnimNodeFlags_Disabled)
{
continue;
}
// Animate node.
pAnimNode->Animate(animContext);
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Render()
{
for (AnimNodes::iterator it = m_nodesNeedToRender.begin(); it != m_nodesNeedToRender.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
pAnimNode->Render();
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Activate()
{
if (m_bActive)
{
return;
}
m_bActive = true;
// Assign animation block to all nodes in this sequence.
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
CUiAnimNode* pAnimNode = static_cast<CUiAnimNode*>(it->get());
pAnimNode->OnReset();
pAnimNode->Activate(true);
}
}
typedef AZStd::fixed_string<512> stack_string;
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Deactivate()
{
if (!m_bActive)
{
return;
}
// Detach animation block from all nodes in this sequence.
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
CUiAnimNode* pAnimNode = static_cast<CUiAnimNode*>(it->get());
pAnimNode->Activate(false);
pAnimNode->OnReset();
}
// Remove a possibly cached game hint associated with this anim sequence.
stack_string sTemp("anim_sequence_");
sTemp += m_name.c_str();
// Audio: Release precached sound
m_bActive = false;
m_precached = false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::PrecacheData(float startTime)
{
PrecacheStatic(startTime);
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::PrecacheStatic(const float startTime)
{
// pre-cache animation keys
for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->PrecacheStatic(startTime);
}
PrecacheDynamic(startTime);
if (m_precached)
{
return;
}
// Try to cache this sequence's game hint if one exists.
stack_string sTemp("anim_sequence_");
sTemp += m_name.c_str();
gEnv->pLog->Log("=== Precaching render data for Ui animation: %s ===", GetName());
m_precached = true;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::PrecacheDynamic(float time)
{
// pre-cache animation keys
for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
static_cast<CUiAnimNode*>(pAnimNode)->PrecacheDynamic(time);
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Reflect(AZ::SerializeContext* serializeContext)
{
serializeContext->Class<CUiAnimSequence>()
->Version(2)
->Field("Name", &CUiAnimSequence::m_name)
->Field("Flags", &CUiAnimSequence::m_flags)
->Field("TimeRange", &CUiAnimSequence::m_timeRange)
->Field("ID", &CUiAnimSequence::m_id)
->Field("Nodes", &CUiAnimSequence::m_nodes)
->Field("Events", &CUiAnimSequence::m_events);
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks, uint32 overrideId, [[maybe_unused]] bool bResetLightAnimSet)
{
if (bLoading)
{
// Load.
RemoveAll();
int sequenceVersion = 0;
xmlNode->getAttr("SequenceVersion", sequenceVersion);
Range timeRange;
m_name = xmlNode->getAttr("Name");
xmlNode->getAttr("Flags", m_flags);
xmlNode->getAttr("StartTime", timeRange.start);
xmlNode->getAttr("EndTime", timeRange.end);
xmlNode->getAttr("ID", m_id);
if (overrideId != 0)
{
m_id = overrideId;
}
INDENT_LOG_DURING_SCOPE(true, "Loading sequence '%s' (start time = %.2f, end time = %.2f) %s ID #%u",
m_name.c_str(), timeRange.start, timeRange.end, overrideId ? "override" : "default", m_id);
// Loading.
XmlNodeRef nodes = xmlNode->findChild("Nodes");
if (nodes)
{
uint32 id;
EUiAnimNodeType nodeType;
for (int i = 0; i < nodes->getChildCount(); ++i)
{
XmlNodeRef childNode = nodes->getChild(i);
childNode->getAttr("Id", id);
static_cast<UiAnimationSystem*>(GetUiAnimationSystem())->SerializeNodeType(nodeType, childNode, bLoading, sequenceVersion, m_flags);
if (nodeType == eUiAnimNodeType_Invalid)
{
continue;
}
IUiAnimNode* pAnimNode = CreateNodeInternal((EUiAnimNodeType)nodeType, id);
if (!pAnimNode)
{
continue;
}
pAnimNode->Serialize(childNode, bLoading, bLoadEmptyTracks);
}
// When all nodes loaded restore group hierarchy
for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
CUiAnimNode* pAnimNode = static_cast<CUiAnimNode*>((*it).get());
pAnimNode->PostLoad();
// And properly adjust the 'm_lastGenId' to prevent the id clash.
if (pAnimNode->GetId() >= (int)m_nextGenId)
{
m_nextGenId = pAnimNode->GetId() + 1;
}
}
}
// Setting the time range must be done after the loading of all nodes
// since it sets the time range of tracks, also.
SetTimeRange(timeRange);
Deactivate();
//ComputeTimeRange();
if (GetOwner())
{
GetOwner()->OnModified();
}
}
else
{
xmlNode->setAttr("SequenceVersion", IUiAnimSequence::kSequenceVersion);
const char* fullname = GetName();
xmlNode->setAttr("Name", fullname); // Save the full path as a name.
xmlNode->setAttr("Flags", m_flags);
xmlNode->setAttr("StartTime", m_timeRange.start);
xmlNode->setAttr("EndTime", m_timeRange.end);
xmlNode->setAttr("ID", m_id);
// Save.
XmlNodeRef nodes = xmlNode->newChild("Nodes");
int num = GetNodeCount();
for (int i = 0; i < num; i++)
{
IUiAnimNode* pAnimNode = GetNode(i);
if (pAnimNode)
{
XmlNodeRef xn = nodes->newChild("Node");
pAnimNode->Serialize(xn, bLoading, true);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::InitPostLoad(IUiAnimationSystem* pUiAnimationSystem, bool remapIds, LyShine::EntityIdMap* entityIdMap)
{
m_pUiAnimationSystem = pUiAnimationSystem;
int nodeCount = GetNodeCount();
for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
IUiAnimNode* animNode = GetNode(nodeIndex);
if (animNode)
{
animNode->InitPostLoad(this, remapIds, entityIdMap);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::SetTimeRange(Range timeRange)
{
m_timeRange = timeRange;
// Set this time range for every track in animation.
// Set time range to be in range of largest animation track.
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* anode = it->get();
anode->SetTimeRange(timeRange);
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::AdjustKeysToTimeRange(const Range& timeRange)
{
float offset = timeRange.start - m_timeRange.start;
// Calculate scale ratio.
float scale = timeRange.Length() / m_timeRange.Length();
m_timeRange = timeRange;
// Set time range to be in range of largest animation track.
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
int trackCount = pAnimNode->GetTrackCount();
for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
{
IUiAnimTrack* track = pAnimNode->GetTrackByIndex(paramIndex);
int nkey = track->GetNumKeys();
for (int k = 0; k < nkey; k++)
{
float keytime = track->GetKeyTime(k);
keytime = offset + keytime * scale;
track->SetKeyTime(k, keytime);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::ComputeTimeRange()
{
Range timeRange = m_timeRange;
// Set time range to be in range of largest animation track.
for (AnimNodes::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
int trackCount = pAnimNode->GetTrackCount();
for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
{
IUiAnimTrack* track = pAnimNode->GetTrackByIndex(paramIndex);
int nkey = track->GetNumKeys();
if (nkey > 0)
{
timeRange.start = std::min(timeRange.start, track->GetKeyTime(0));
timeRange.end = std::max(timeRange.end, track->GetKeyTime(nkey - 1));
}
}
}
if (timeRange.start > 0)
{
timeRange.start = 0;
}
m_timeRange = timeRange;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::AddTrackEvent(const char* szEvent)
{
AZ_Assert(szEvent && szEvent[0], "Track Event is nullptr.");
if (stl::push_back_unique(m_events, szEvent))
{
NotifyTrackEvent(IUiTrackEventListener::eTrackEventReason_Added, szEvent);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::RemoveTrackEvent(const char* szEvent)
{
AZ_Assert(szEvent && szEvent[0], "Track Event is nullptr.");
if (stl::find_and_erase(m_events, szEvent))
{
NotifyTrackEvent(IUiTrackEventListener::eTrackEventReason_Removed, szEvent);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::RenameTrackEvent(const char* szEvent, const char* szNewEvent)
{
AZ_Assert(szEvent && szEvent[0], "Track Event is nullptr.");
AZ_Assert(szNewEvent && szNewEvent[0], "New Track Event is nullptr.");
for (size_t i = 0; i < m_events.size(); ++i)
{
if (m_events[i] == szEvent)
{
m_events[i] = szNewEvent;
NotifyTrackEvent(IUiTrackEventListener::eTrackEventReason_Renamed, szEvent, szNewEvent);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::MoveUpTrackEvent(const char* szEvent)
{
AZ_Assert(szEvent && szEvent[0], "Track Event is nullptr.");
for (size_t i = 0; i < m_events.size(); ++i)
{
if (m_events[i] == szEvent)
{
AZ_Assert(i > 0, "Event already first in Track.");
if (i > 0)
{
std::swap(m_events[i - 1], m_events[i]);
NotifyTrackEvent(IUiTrackEventListener::eTrackEventReason_MovedUp, szEvent);
}
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::MoveDownTrackEvent(const char* szEvent)
{
AZ_Assert(szEvent && szEvent[0], "Track Event is nullptr.");
for (size_t i = 0; i < m_events.size(); ++i)
{
if (m_events[i] == szEvent)
{
AZ_Assert(i < m_events.size() - 1, "Event already last in Track.");
if (i < m_events.size() - 1)
{
std::swap(m_events[i], m_events[i + 1]);
NotifyTrackEvent(IUiTrackEventListener::eTrackEventReason_MovedDown, szEvent);
}
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::ClearTrackEvents()
{
m_events.clear();
}
//////////////////////////////////////////////////////////////////////////
int CUiAnimSequence::GetTrackEventsCount() const
{
return static_cast<int>(m_events.size());
}
//////////////////////////////////////////////////////////////////////////
char const* CUiAnimSequence::GetTrackEvent(int iIndex) const
{
char const* szResult = NULL;
const bool bValid = (iIndex >= 0 && iIndex < GetTrackEventsCount());
AZ_Assert(bValid, "Track Event index out of range.");
if (bValid)
{
szResult = m_events[iIndex].c_str();
}
return szResult;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::NotifyTrackEvent(IUiTrackEventListener::ETrackEventReason reason,
const char* event, const char* param)
{
// Notify listeners
for (TUiTrackEventListeners::iterator j = m_listeners.begin(); j != m_listeners.end(); ++j)
{
(*j)->OnTrackEvent(this, reason, event, (void*)param);
}
// Pass to Animation System to notify via EBus
GetUiAnimationSystem()->NotifyTrackEventListeners(event, param, this);
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::TriggerTrackEvent(const char* event, const char* param)
{
NotifyTrackEvent(IUiTrackEventListener::eTrackEventReason_Triggered, event, param);
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::AddTrackEventListener(IUiTrackEventListener* pListener)
{
if (AZStd::find(m_listeners.begin(), m_listeners.end(), pListener) == m_listeners.end())
{
m_listeners.push_back(pListener);
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::RemoveTrackEventListener(IUiTrackEventListener* pListener)
{
TUiTrackEventListeners::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
if (it != m_listeners.end())
{
m_listeners.erase(it);
}
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimSequence::FindNodeById(int nNodeId)
{
for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
if (static_cast<CUiAnimNode*>(pAnimNode)->GetId() == nNodeId)
{
return pAnimNode;
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimSequence::FindNodeByName(const char* sNodeName, const IUiAnimNode* pParentDirector)
{
for (AnimNodes::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
// Case insensitive name comparison.
if (_stricmp(((CUiAnimNode*)pAnimNode)->GetNameFast(), sNodeName) == 0)
{
bool bParentDirectorCheck = pAnimNode->HasDirectorAsParent() == pParentDirector;
if (bParentDirectorCheck)
{
return pAnimNode;
}
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::ReorderNode(IUiAnimNode* pNode, IUiAnimNode* pPivotNode, bool bNext)
{
if (pNode == pPivotNode || !pNode)
{
return;
}
AZStd::intrusive_ptr<IUiAnimNode> pTempHolder(pNode); // Keep reference to node so it is not deleted by erasing from list.
stl::find_and_erase_if(m_nodes, [pNode](const AZStd::intrusive_ptr<IUiAnimNode>& sp) { return sp.get() == pNode; });
AnimNodes::iterator it;
for (it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IUiAnimNode* pAnimNode = it->get();
if (pAnimNode == pPivotNode)
{
if (bNext)
{
m_nodes.insert(it + 1, AZStd::intrusive_ptr<IUiAnimNode>(pNode));
}
else
{
m_nodes.insert(it, AZStd::intrusive_ptr<IUiAnimNode>(pNode));
}
break;
}
}
if (it == m_nodes.end())
{
m_nodes.insert(m_nodes.begin(), AZStd::intrusive_ptr<IUiAnimNode>(pNode));
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::CopyNodeChildren(XmlNodeRef& xmlNode, IUiAnimNode* pAnimNode)
{
for (int k = 0; k < GetNodeCount(); ++k)
{
if (GetNode(k)->GetParent() == pAnimNode)
{
XmlNodeRef childNode = xmlNode->newChild("Node");
GetNode(k)->Serialize(childNode, false, true);
if (GetNode(k)->GetType() == eUiAnimNodeType_Group
|| pAnimNode->GetType() == eUiAnimNodeType_Director)
{
CopyNodeChildren(xmlNode, GetNode(k));
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::CopyNodes(XmlNodeRef& xmlNode, IUiAnimNode** pSelectedNodes, uint32 count)
{
for (uint32 i = 0; i < count; ++i)
{
IUiAnimNode* pAnimNode = pSelectedNodes[i];
if (pAnimNode)
{
XmlNodeRef xn = xmlNode->newChild("Node");
pAnimNode->Serialize(xn, false, true);
// If it is a group node, copy its children also.
if (pAnimNode->GetType() == eUiAnimNodeType_Group || pAnimNode->GetType() == eUiAnimNodeType_Director)
{
CopyNodeChildren(xmlNode, pAnimNode);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::PasteNodes(const XmlNodeRef& xmlNode, IUiAnimNode* pParent)
{
int type, id;
std::map<int, IUiAnimNode*> idToNode;
for (int i = 0; i < xmlNode->getChildCount(); i++)
{
XmlNodeRef xn = xmlNode->getChild(i);
if (!xn->getAttr("Type", type))
{
continue;
}
xn->getAttr("Id", id);
IUiAnimNode* node = CreateNode((EUiAnimNodeType)type);
if (!node)
{
continue;
}
idToNode[id] = node;
xn->setAttr("Id", static_cast<CUiAnimNode*>(node)->GetId());
node->Serialize(xn, true, true);
int parentId = 0;
if (xn->getAttr("ParentNode", parentId))
{
node->SetParent(idToNode[parentId]);
}
else
// This means a top-level node.
{
if (pParent)
{
node->SetParent(pParent);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::AddNodeNeedToRender(IUiAnimNode* pNode)
{
assert(pNode != 0);
return stl::push_back_unique(m_nodesNeedToRender, AZStd::intrusive_ptr<IUiAnimNode>(pNode));
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::RemoveNodeNeedToRender(IUiAnimNode* pNode)
{
assert(pNode != 0);
stl::find_and_erase_if(m_nodesNeedToRender, [pNode](const AZStd::intrusive_ptr<IUiAnimNode>& sp) { return sp.get() == pNode; });
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimSequence::SetActiveDirector(IUiAnimNode* pDirectorNode)
{
if (!pDirectorNode)
{
return;
}
AZ_Assert(pDirectorNode->GetType() == eUiAnimNodeType_Director, "New Director Node is not Director Type.");
if (pDirectorNode->GetType() != eUiAnimNodeType_Director)
{
return; // It's not a director node.
}
if (pDirectorNode->GetSequence() != this)
{
return; // It's not a node belong to this sequence.
}
m_pActiveDirector = pDirectorNode;
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimSequence::GetActiveDirector() const
{
return m_pActiveDirector;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimSequence::IsAncestorOf(const IUiAnimSequence* pSequence) const
{
AZ_Assert(this != pSequence, "Checked if UiAnimSequence was ancestor of itself.");
if (this == pSequence)
{
return true;
}
// UI_ANIMATION_REVISIT: was only doing anything for sequence tracks
return false;
}