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/AnimNode.cpp

807 lines
24 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 <AzCore/Serialization/SerializeContext.h>
#include "AnimNode.h"
#include "AnimTrack.h"
#include "AnimSequence.h"
#include "AnimSplineTrack.h"
#include "BoolTrack.h"
#include "TrackEventTrack.h"
#include "CompoundSplineTrack.h"
#include <AzCore/std/sort.h>
#include <ctime>
//////////////////////////////////////////////////////////////////////////
// Old deprecated IDs
//////////////////////////////////////////////////////////////////////////
#define APARAM_CHARACTER4 (eUiAnimParamType_User + 0x10)
#define APARAM_CHARACTER5 (eUiAnimParamType_User + 0x11)
#define APARAM_CHARACTER6 (eUiAnimParamType_User + 0x12)
#define APARAM_CHARACTER7 (eUiAnimParamType_User + 0x13)
#define APARAM_CHARACTER8 (eUiAnimParamType_User + 0x14)
#define APARAM_CHARACTER9 (eUiAnimParamType_User + 0x15)
#define APARAM_CHARACTER10 (eUiAnimParamType_User + 0x16)
#define APARAM_EXPRESSION4 (eUiAnimParamType_User + 0x20)
#define APARAM_EXPRESSION5 (eUiAnimParamType_User + 0x21)
#define APARAM_EXPRESSION6 (eUiAnimParamType_User + 0x22)
#define APARAM_EXPRESSION7 (eUiAnimParamType_User + 0x23)
#define APARAM_EXPRESSION8 (eUiAnimParamType_User + 0x24)
#define APARAM_EXPRESSION9 (eUiAnimParamType_User + 0x25)
#define APARAM_EXPRESSION10 (eUiAnimParamType_User + 0x26)
//////////////////////////////////////////////////////////////////////////
static const EUiAnimCurveType DEFAULT_TRACK_TYPE = eUiAnimCurveType_BezierFloat;
// Old serialization values that are no longer
// defined in IUiAnimationSystem.h, but needed for conversion:
static const int OLD_ACURVE_GOTO = 21;
static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95;
static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96;
static const int OLD_APARAM_PARTICLE_SCALE = 97;
static const int OLD_APARAM_PARTICLE_SPEED_SCALE = 98;
static const int OLD_APARAM_PARTICLE_STRENGTH = 99;
//////////////////////////////////////////////////////////////////////////
// CUiAnimNode.
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::Activate([[maybe_unused]] bool bActivate)
{
}
//////////////////////////////////////////////////////////////////////////
int CUiAnimNode::GetTrackCount() const
{
return static_cast<int>(m_tracks.size());
}
AZStd::string CUiAnimNode::GetParamName(const CUiAnimParamType& paramType) const
{
SParamInfo info;
if (GetParamInfoFromType(paramType, info))
{
return info.name;
}
return "Unknown";
}
EUiAnimValue CUiAnimNode::GetParamValueType(const CUiAnimParamType& paramType) const
{
SParamInfo info;
if (GetParamInfoFromType(paramType, info))
{
return info.valueType;
}
return eUiAnimValue_Unknown;
}
IUiAnimNode::ESupportedParamFlags CUiAnimNode::GetParamFlags(const CUiAnimParamType& paramType) const
{
SParamInfo info;
if (GetParamInfoFromType(paramType, info))
{
return info.flags;
}
return IUiAnimNode::ESupportedParamFlags(0);
}
IUiAnimTrack* CUiAnimNode::GetTrackForParameter(const CUiAnimParamType& paramType) const
{
for (int i = 0, num = (int)m_tracks.size(); i < num; i++)
{
if (m_tracks[i]->GetParameterType() == paramType)
{
return m_tracks[i].get();
}
// Search the sub-tracks also if any.
for (int k = 0; k < m_tracks[i]->GetSubTrackCount(); ++k)
{
if (m_tracks[i]->GetSubTrack(k)->GetParameterType() == paramType)
{
return m_tracks[i]->GetSubTrack(k);
}
}
}
return 0;
}
IUiAnimTrack* CUiAnimNode::GetTrackForParameter(const CUiAnimParamType& paramType, uint32 index) const
{
SParamInfo paramInfo;
GetParamInfoFromType(paramType, paramInfo);
if ((paramInfo.flags & IUiAnimNode::eSupportedParamFlags_MultipleTracks) == 0)
{
return GetTrackForParameter(paramType);
}
uint32 count = 0;
for (int i = 0, num = (int)m_tracks.size(); i < num; i++)
{
if (m_tracks[i]->GetParameterType() == paramType && count++ == index)
{
return m_tracks[i].get();
}
// For this case, no subtracks are considered.
}
return 0;
}
uint32 CUiAnimNode::GetTrackParamIndex(const IUiAnimTrack* track) const
{
AZ_Assert(track, "Track is nullptr.");
uint32 index = 0;
CUiAnimParamType paramType = track->GetParameterType();
SParamInfo paramInfo;
GetParamInfoFromType(paramType, paramInfo);
if ((paramInfo.flags & IUiAnimNode::eSupportedParamFlags_MultipleTracks) == 0)
{
return 0;
}
for (int i = 0, num = (int)m_tracks.size(); i < num; i++)
{
if (m_tracks[i].get() == track)
{
return index;
}
if (m_tracks[i]->GetParameterType() == paramType)
{
++index;
}
// For this case, no subtracks are considered.
}
AZ_Assert(false, "CUiAnimNode::GetTrackParamIndex() called with an invalid argument!");
return 0;
}
IUiAnimTrack* CUiAnimNode::GetTrackByIndex(int nIndex) const
{
AZ_Assert(nIndex >= 0 && nIndex < static_cast<int>(m_tracks.size()), "Track index out of range.");
if (nIndex < 0 || nIndex >= static_cast<int>(m_tracks.size()))
{
return NULL;
}
return m_tracks[nIndex].get();
}
void CUiAnimNode::SetTrack(const CUiAnimParamType& paramType, IUiAnimTrack* track)
{
if (track)
{
for (unsigned int i = 0; i < m_tracks.size(); i++)
{
if (m_tracks[i]->GetParameterType() == paramType)
{
m_tracks[i].reset(track);
return;
}
}
AddTrack(track);
}
else
{
// Remove track at this id.
for (unsigned int i = 0; i < m_tracks.size(); i++)
{
if (m_tracks[i]->GetParameterType() == paramType)
{
m_tracks.erase(m_tracks.begin() + i);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::TrackOrder(const AZStd::intrusive_ptr<IUiAnimTrack>& left, const AZStd::intrusive_ptr<IUiAnimTrack>& right)
{
return left->GetParameterType() < right->GetParameterType();
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::AddTrack(IUiAnimTrack* track)
{
RegisterTrack(track);
m_tracks.push_back(AZStd::intrusive_ptr<IUiAnimTrack>(track));
SortTracks();
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::RegisterTrack(IUiAnimTrack* track)
{
track->SetTimeRange(GetSequence()->GetTimeRange());
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::SortTracks()
{
AZStd::insertion_sort(m_tracks.begin(), m_tracks.end(), TrackOrder);
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::RemoveTrack(IUiAnimTrack* track)
{
for (unsigned int i = 0; i < m_tracks.size(); i++)
{
if (m_tracks[i].get() == track)
{
m_tracks.erase(m_tracks.begin() + i);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::Reflect(AZ::SerializeContext* serializeContext)
{
// we do not currently serialize node type because all nodes are the same type (AzEntityNode)
serializeContext->Class<CUiAnimNode>()
->Version(2)
->Field("ID", &CUiAnimNode::m_id)
->Field("Parent", &CUiAnimNode::m_parentNodeId)
->Field("Name", &CUiAnimNode::m_name)
->Field("Flags", &CUiAnimNode::m_flags)
->Field("Tracks", &CUiAnimNode::m_tracks)
->Field("Type", &CUiAnimNode::m_nodeType);
}
//////////////////////////////////////////////////////////////////////////
IUiAnimTrack* CUiAnimNode::CreateTrackInternal(const CUiAnimParamType& paramType, EUiAnimCurveType trackType, EUiAnimValue valueType)
{
if (valueType == eUiAnimValue_Unknown)
{
SParamInfo info;
// Try to get info from paramType, else we can't determine the track data type
if (!GetParamInfoFromType(paramType, info))
{
return 0;
}
valueType = info.valueType;
}
IUiAnimTrack* track = NULL;
switch (paramType.GetType())
{
// Create sub-classed tracks
case eUiAnimParamType_TrackEvent:
track = aznew CUiTrackEventTrack(m_pSequence->GetTrackEventStringTable());
break;
case eUiAnimParamType_Float:
track = CreateTrackInternalFloat(trackType);
break;
default:
// Create standard tracks
switch (valueType)
{
case eUiAnimValue_Float:
track = CreateTrackInternalFloat(trackType);
break;
case eUiAnimValue_RGB:
case eUiAnimValue_Vector:
track = CreateTrackInternalVector(trackType, paramType, valueType);
break;
case eUiAnimValue_Quat:
track = CreateTrackInternalQuat(trackType, paramType);
break;
case eUiAnimValue_Bool:
track = aznew UiBoolTrack;
break;
case eUiAnimValue_Vector2:
track = CreateTrackInternalVector2(paramType);
break;
case eUiAnimValue_Vector3:
track = CreateTrackInternalVector3(paramType);
break;
case eUiAnimValue_Vector4:
track = CreateTrackInternalVector4(paramType);
break;
}
}
if (track)
{
track->SetParameterType(paramType);
AddTrack(track);
}
return track;
}
//////////////////////////////////////////////////////////////////////////
IUiAnimTrack* CUiAnimNode::CreateTrack(const CUiAnimParamType& paramType)
{
IUiAnimTrack* track = CreateTrackInternal(paramType, DEFAULT_TRACK_TYPE, eUiAnimValue_Unknown);
return track;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::SerializeUiAnims(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks)
{
if (bLoading)
{
// Delete all tracks.
stl::free_container(m_tracks);
CUiAnimNode::SParamInfo info;
// Loading.
int paramTypeVersion = 0;
xmlNode->getAttr("paramIdVersion", paramTypeVersion);
CUiAnimParamType paramType;
const int num = xmlNode->getChildCount();
for (int i = 0; i < num; i++)
{
XmlNodeRef trackNode = xmlNode->getChild(i);
paramType.Serialize(GetUiAnimationSystem(), trackNode, bLoading, paramTypeVersion);
int curveType = eUiAnimCurveType_Unknown;
trackNode->getAttr("Type", curveType);
int valueType = eUiAnimValue_Unknown;
trackNode->getAttr("ValueType", valueType);
IUiAnimTrack* track = CreateTrackInternal(paramType, (EUiAnimCurveType)curveType, (EUiAnimValue)valueType);
if (track)
{
UiAnimParamData paramData;
paramData.Serialize(GetUiAnimationSystem(), trackNode, bLoading);
track->SetParamData(paramData);
if (!track->Serialize(GetUiAnimationSystem(), trackNode, bLoading, bLoadEmptyTracks))
{
// Boolean tracks must always be loaded even if empty.
if (track->GetValueType() != eUiAnimValue_Bool)
{
RemoveTrack(track);
}
}
}
}
}
else
{
// Saving.
xmlNode->setAttr("paramIdVersion", CUiAnimParamType::kParamTypeVersion);
for (unsigned int i = 0; i < m_tracks.size(); i++)
{
IUiAnimTrack* track = m_tracks[i].get();
if (track)
{
CUiAnimParamType paramType = track->GetParameterType();
XmlNodeRef trackNode = xmlNode->newChild("Track");
paramType.Serialize(GetUiAnimationSystem(), trackNode, bLoading);
UiAnimParamData paramData = track->GetParamData();
paramData.Serialize(GetUiAnimationSystem(), trackNode, bLoading);
int nTrackType = track->GetCurveType();
trackNode->setAttr("Type", nTrackType);
track->Serialize(GetUiAnimationSystem(), trackNode, bLoading);
int valueType = track->GetValueType();
trackNode->setAttr("ValueType", valueType);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::SetTimeRange(Range timeRange)
{
for (unsigned int i = 0; i < m_tracks.size(); i++)
{
if (m_tracks[i])
{
m_tracks[i]->SetTimeRange(timeRange);
}
}
}
//////////////////////////////////////////////////////////////////////////
// AZ::Serialization requires a default constructor
CUiAnimNode::CUiAnimNode()
: CUiAnimNode(0, eUiAnimNodeType_Invalid)
{
}
//////////////////////////////////////////////////////////////////////////
// explicit copy constructor is required to prevent compiler's generated copy constructor
// from calling AZStd::mutex's private copy constructor
CUiAnimNode::CUiAnimNode(const CUiAnimNode& other)
: m_refCount(0)
, m_id(0) // don't copy id - these should be unique
, m_parentNodeId(other.m_parentNodeId)
, m_nodeType(other.m_nodeType)
, m_pOwner(other.m_pOwner)
, m_pSequence(other.m_pSequence)
, m_flags(other.m_flags)
, m_pParentNode(other.m_pParentNode)
, m_nLoadedParentNodeId(other.m_nLoadedParentNodeId)
{
// m_bIgnoreSetParam not copied
}
//////////////////////////////////////////////////////////////////////////
CUiAnimNode::CUiAnimNode(const int id, EUiAnimNodeType nodeType)
: m_refCount(0)
, m_id(id)
, m_parentNodeId(0)
, m_nodeType(nodeType)
{
m_pOwner = 0;
m_pSequence = 0;
m_flags = 0;
m_bIgnoreSetParam = false;
m_pParentNode = 0;
m_nLoadedParentNodeId = 0;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimNode::~CUiAnimNode()
{
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::add_ref()
{
++m_refCount;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::release()
{
if (--m_refCount <= 0)
{
delete this;
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::SetFlags(int flags)
{
m_flags = flags;
}
//////////////////////////////////////////////////////////////////////////
int CUiAnimNode::GetFlags() const
{
return m_flags;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::Animate([[maybe_unused]] SUiAnimContext& ec)
{
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::IsParamValid(const CUiAnimParamType& paramType) const
{
SParamInfo info;
if (GetParamInfoFromType(paramType, info))
{
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::SetParamValue(float time, CUiAnimParamType param, float value)
{
if (m_bIgnoreSetParam)
{
return true;
}
IUiAnimTrack* track = GetTrackForParameter(param);
if (track && track->GetValueType() == eUiAnimValue_Float)
{
// Float track.
bool bDefault = !(GetUiAnimationSystem()->IsRecording() && (m_flags & eUiAnimNodeFlags_EntitySelected)); // Only selected nodes can be recorded
track->SetValue(time, value, bDefault);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::SetParamValue(float time, CUiAnimParamType param, const Vec3& value)
{
if (m_bIgnoreSetParam)
{
return true;
}
UiCompoundSplineTrack* track = static_cast<UiCompoundSplineTrack*>(GetTrackForParameter(param));
if (track && track->GetValueType() == eUiAnimValue_Vector)
{
// Vec3 track.
bool bDefault = !(GetUiAnimationSystem()->IsRecording() && (m_flags & eUiAnimNodeFlags_EntitySelected)); // Only selected nodes can be recorded
track->SetValue(time, value, bDefault);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::SetParamValue(float time, CUiAnimParamType param, const Vec4& value)
{
if (m_bIgnoreSetParam)
{
return true;
}
UiCompoundSplineTrack* track = static_cast<UiCompoundSplineTrack*>(GetTrackForParameter(param));
if (track && track->GetValueType() == eUiAnimValue_Vector4)
{
// Vec4 track.
bool bDefault = !(GetUiAnimationSystem()->IsRecording() && (m_flags & eUiAnimNodeFlags_EntitySelected)); // Only selected nodes can be recorded
track->SetValue(time, value, bDefault);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::GetParamValue(float time, CUiAnimParamType param, float& value)
{
IUiAnimTrack* track = GetTrackForParameter(param);
if (track && track->GetValueType() == eUiAnimValue_Float && track->GetNumKeys() > 0)
{
// Float track.
track->GetValue(time, value);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::GetParamValue(float time, CUiAnimParamType param, Vec3& value)
{
UiCompoundSplineTrack* track = static_cast<UiCompoundSplineTrack*>(GetTrackForParameter(param));
if (track && track->GetValueType() == eUiAnimValue_Vector && track->GetNumKeys() > 0)
{
// Vec3 track.
track->GetValue(time, value);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CUiAnimNode::GetParamValue(float time, CUiAnimParamType param, Vec4& value)
{
UiCompoundSplineTrack* track = static_cast<UiCompoundSplineTrack*>(GetTrackForParameter(param));
if (track && track->GetValueType() == eUiAnimValue_Vector4 && track->GetNumKeys() > 0)
{
// Vec4 track.
track->GetValue(time, value);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks)
{
if (bLoading)
{
xmlNode->getAttr("Id", m_id);
const char* name = xmlNode->getAttr("Name");
int flags;
if (xmlNode->getAttr("Flags", flags))
{
// Don't load expanded or selected flags
flags = flags & ~(eUiAnimNodeFlags_Expanded | eUiAnimNodeFlags_EntitySelected);
SetFlags(flags);
}
SetName(name);
m_nLoadedParentNodeId = 0;
xmlNode->getAttr("ParentNode", m_nLoadedParentNodeId);
}
else
{
m_nLoadedParentNodeId = 0;
xmlNode->setAttr("Id", m_id);
EUiAnimNodeType nodeType = GetType();
static_cast<UiAnimationSystem*>(GetUiAnimationSystem())->SerializeNodeType(nodeType, xmlNode, bLoading, IUiAnimSequence::kSequenceVersion, m_flags);
xmlNode->setAttr("Name", GetName().c_str());
// Don't store expanded or selected flags
int flags = GetFlags() & ~(eUiAnimNodeFlags_Expanded | eUiAnimNodeFlags_EntitySelected);
xmlNode->setAttr("Flags", flags);
if (m_pParentNode)
{
xmlNode->setAttr("ParentNode", static_cast<CUiAnimNode*>(m_pParentNode)->GetId());
}
}
SerializeUiAnims(xmlNode, bLoading, bLoadEmptyTracks);
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::InitPostLoad(IUiAnimSequence* pSequence, [[maybe_unused]] bool remapIds, [[maybe_unused]] LyShine::EntityIdMap* entityIdMap)
{
m_pSequence = pSequence;
m_pParentNode = ((CUiAnimSequence*)m_pSequence)->FindNodeById(m_parentNodeId);
// fix up animNode pointers and time ranges on tracks, then sort them
for (unsigned int i = 0; i < m_tracks.size(); i++)
{
IUiAnimTrack* track = m_tracks[i].get();
RegisterTrack(track);
track->InitPostLoad(pSequence);
}
SortTracks();
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::SetNodeOwner(IUiAnimNodeOwner* pOwner)
{
m_pOwner = pOwner;
if (pOwner)
{
pOwner->OnNodeUiAnimated(this);
}
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::PostLoad()
{
if (m_nLoadedParentNodeId)
{
IUiAnimNode* pParentNode = ((CUiAnimSequence*)m_pSequence)->FindNodeById(m_nLoadedParentNodeId);
m_pParentNode = pParentNode;
m_parentNodeId = m_nLoadedParentNodeId; // adding as a temporary fix while we support both serialization methods
m_nLoadedParentNodeId = 0;
}
}
//////////////////////////////////////////////////////////////////////////
Matrix34 CUiAnimNode::GetReferenceMatrix() const
{
static Matrix34 tm(IDENTITY);
return tm;
}
IUiAnimTrack* CUiAnimNode::CreateTrackInternalFloat([[maybe_unused]] int trackType) const
{
// Don't need trackType any more
return aznew C2DSplineTrack;
}
IUiAnimTrack* CUiAnimNode::CreateTrackInternalVector([[maybe_unused]] EUiAnimCurveType trackType, [[maybe_unused]] const CUiAnimParamType& paramType, [[maybe_unused]] const EUiAnimValue animValue) const
{
// Don't need trackType any more
CUiAnimParamType subTrackParamTypes[MAX_SUBTRACKS];
for (unsigned int i = 0; i < MAX_SUBTRACKS; ++i)
{
subTrackParamTypes[i] = eUiAnimParamType_AzComponentField;
}
return aznew UiCompoundSplineTrack(3, eUiAnimValue_Vector, subTrackParamTypes);
}
IUiAnimTrack* CUiAnimNode::CreateTrackInternalQuat([[maybe_unused]] EUiAnimCurveType trackType, [[maybe_unused]] const CUiAnimParamType& paramType) const
{
// UI_ANIMATION_REVISIT - my may want quat support at some point
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
IUiAnimTrack* CUiAnimNode::CreateTrackInternalVector2([[maybe_unused]] const CUiAnimParamType& paramType) const
{
IUiAnimTrack* track;
CUiAnimParamType subTrackParamTypes[MAX_SUBTRACKS];
for (unsigned int i = 0; i < MAX_SUBTRACKS; ++i)
{
subTrackParamTypes[i] = eUiAnimParamType_Float;
}
track = aznew UiCompoundSplineTrack(2, eUiAnimValue_Vector2, subTrackParamTypes);
return track;
}
IUiAnimTrack* CUiAnimNode::CreateTrackInternalVector3([[maybe_unused]] const CUiAnimParamType& paramType) const
{
IUiAnimTrack* track;
CUiAnimParamType subTrackParamTypes[MAX_SUBTRACKS];
for (unsigned int i = 0; i < MAX_SUBTRACKS; ++i)
{
subTrackParamTypes[i] = eUiAnimParamType_Float;
}
track = aznew UiCompoundSplineTrack(3, eUiAnimValue_Vector3, subTrackParamTypes);
return track;
}
//////////////////////////////////////////////////////////////////////////
IUiAnimTrack* CUiAnimNode::CreateTrackInternalVector4([[maybe_unused]] const CUiAnimParamType& paramType) const
{
IUiAnimTrack* track;
CUiAnimParamType subTrackParamTypes[MAX_SUBTRACKS];
for (unsigned int i = 0; i < MAX_SUBTRACKS; ++i)
{
subTrackParamTypes[i] = eUiAnimParamType_Float;
}
track = aznew UiCompoundSplineTrack(4, eUiAnimValue_Vector4, subTrackParamTypes);
return track;
}
//////////////////////////////////////////////////////////////////////////
void CUiAnimNode::SetParent(IUiAnimNode* pParent)
{
m_pParentNode = pParent;
if (pParent)
{
m_parentNodeId = static_cast<CUiAnimNode*>(m_pParentNode)->GetId();
}
else
{
m_parentNodeId = 0;
}
}
//////////////////////////////////////////////////////////////////////////
IUiAnimNode* CUiAnimNode::HasDirectorAsParent() const
{
IUiAnimNode* pParent = GetParent();
while (pParent)
{
if (pParent->GetType() == eUiAnimNodeType_Director)
{
return pParent;
}
// There are some invalid data.
if (pParent->GetParent() == pParent)
{
pParent->SetParent(NULL);
return NULL;
}
pParent = pParent->GetParent();
}
return NULL;
}