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

630 lines
18 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 "CompoundSplineTrack.h"
#include "AnimSplineTrack.h"
UiCompoundSplineTrack::UiCompoundSplineTrack(int nDims, EUiAnimValue inValueType, CUiAnimParamType subTrackParamTypes[MAX_SUBTRACKS])
: m_refCount(0)
{
assert(nDims > 0 && nDims <= MAX_SUBTRACKS);
m_nDimensions = nDims;
m_valueType = inValueType;
m_nParamType = eUiAnimNodeType_Invalid;
m_flags = 0;
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i].reset(aznew C2DSplineTrack());
m_subTracks[i]->SetParameterType(subTrackParamTypes[i]);
if (inValueType == eUiAnimValue_RGB)
{
m_subTracks[i]->SetKeyValueRange(0.0f, 255.f);
}
}
m_subTrackNames[0] = "X";
m_subTrackNames[1] = "Y";
m_subTrackNames[2] = "Z";
m_subTrackNames[3] = "W";
#ifdef UI_ANIMATION_SYSTEM_SUPPORT_EDITING
m_bCustomColorSet = false;
#endif
}
//////////////////////////////////////////////////////////////////////////
// Need default constructor for AZ Serialization
UiCompoundSplineTrack::UiCompoundSplineTrack()
: m_refCount(0)
, m_nDimensions(0)
#ifdef UI_ANIMATION_SYSTEM_SUPPORT_EDITING
, m_bCustomColorSet(false)
#endif
{
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetTimeRange(const Range& timeRange)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetTimeRange(timeRange);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::PrepareNodeForSubTrackSerialization(XmlNodeRef& subTrackNode, XmlNodeRef& xmlNode, int i, bool bLoading)
{
assert(!bLoading || xmlNode->getChildCount() == m_nDimensions);
if (bLoading)
{
subTrackNode = xmlNode->getChild(i);
// First, check its version.
if (strcmp(subTrackNode->getTag(), "SubTrack") == 0)
// So, it's an old format.
{
#if 0
CUiAnimParamType paramType = m_subTracks[i]->GetParameterType();
// Recreate sub tracks as the old format.
m_subTracks[i] = new CTcbFloatTrack;
m_subTracks[i]->SetParameterType(paramType);
#endif
}
}
else
{
if (m_subTracks[i]->GetCurveType() == eUiAnimCurveType_BezierFloat)
{
// It's a new 2D Bezier curve.
subTrackNode = xmlNode->newChild("NewSubTrack");
}
#if 0
else
// Old TCB spline
{
assert(m_subTracks[i]->GetCurveType() == eUiAnimCurveType_TCBFloat);
subTrackNode = xmlNode->newChild("SubTrack");
}
#endif
}
}
//////////////////////////////////////////////////////////////////////////
bool UiCompoundSplineTrack::Serialize(IUiAnimationSystem* uiAnimationSystem, XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks /*=true */)
{
#ifdef UI_ANIMATION_SYSTEM_SUPPORT_EDITING
if (bLoading)
{
int flags = m_flags;
xmlNode->getAttr("Flags", flags);
SetFlags(flags);
xmlNode->getAttr("HasCustomColor", m_bCustomColorSet);
if (m_bCustomColorSet)
{
unsigned int abgr;
xmlNode->getAttr("CustomColor", abgr);
m_customColor = ColorB(abgr);
}
}
else
{
int flags = GetFlags();
xmlNode->setAttr("Flags", flags);
xmlNode->setAttr("HasCustomColor", m_bCustomColorSet);
if (m_bCustomColorSet)
{
xmlNode->setAttr("CustomColor", m_customColor.pack_abgr8888());
}
}
#endif
for (int i = 0; i < m_nDimensions; i++)
{
XmlNodeRef subTrackNode;
PrepareNodeForSubTrackSerialization(subTrackNode, xmlNode, i, bLoading);
if (bLoading)
{
CUiAnimParamType paramType;
paramType.Serialize(uiAnimationSystem, subTrackNode, bLoading);
m_subTracks[i]->SetParameterType(paramType);
UiAnimParamData paramData;
paramData.Serialize(uiAnimationSystem, subTrackNode, bLoading);
m_subTracks[i]->SetParamData(paramData);
}
else
{
CUiAnimParamType paramType = m_subTracks[i]->GetParameterType();
paramType.Serialize(uiAnimationSystem, subTrackNode, bLoading);
UiAnimParamData paramData = m_subTracks[i]->GetParamData();
paramData.Serialize(uiAnimationSystem, subTrackNode, bLoading);
}
m_subTracks[i]->Serialize(uiAnimationSystem, subTrackNode, bLoading, bLoadEmptyTracks);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool UiCompoundSplineTrack::SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected /*=false*/, float fTimeOffset /*=0*/)
{
for (int i = 0; i < m_nDimensions; i++)
{
XmlNodeRef subTrackNode;
PrepareNodeForSubTrackSerialization(subTrackNode, xmlNode, i, bLoading);
m_subTracks[i]->SerializeSelection(subTrackNode, bLoading, bCopySelected, fTimeOffset);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, float& value)
{
for (int i = 0; i < 1 && i < m_nDimensions; i++)
{
m_subTracks[i]->GetValue(time, value);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, Vec3& value)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value[i];
m_subTracks[i]->GetValue(time, v);
value[i] = v;
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, Vec4& value)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value[i];
m_subTracks[i]->GetValue(time, v);
value[i] = v;
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, Quat& value)
{
if (m_nDimensions == 3)
{
// Assume Euler Angles XYZ
float angles[3] = {0, 0, 0};
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->GetValue(time, angles[i]);
}
value = Quat::CreateRotationXYZ(Ang3(DEG2RAD(angles[0]), DEG2RAD(angles[1]), DEG2RAD(angles[2])));
}
else
{
assert(0);
value.SetIdentity();
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, AZ::Vector2& value)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value.GetElement(i);
m_subTracks[i]->GetValue(time, v);
value.SetElement(i, v);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, AZ::Vector3& value)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value.GetElement(i);
m_subTracks[i]->GetValue(time, v);
value.SetElement(i, v);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, AZ::Vector4& value)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value.GetElement(i);
m_subTracks[i]->GetValue(time, v);
value.SetElement(i, v);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::GetValue(float time, AZ::Color& value)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value.GetElement(i);
m_subTracks[i]->GetValue(time, v);
value.SetElement(i, v);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const float& value, bool bDefault)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value, bDefault);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const Vec3& value, bool bDefault)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value[i], bDefault);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const Vec4& value, bool bDefault)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value[i], bDefault);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const Quat& value, bool bDefault)
{
if (m_nDimensions == 3)
{
// Assume Euler Angles XYZ
Ang3 angles = Ang3::GetAnglesXYZ(value);
for (int i = 0; i < 3; i++)
{
float degree = RAD2DEG(angles[i]);
if (false == bDefault)
{
// Try to prefer the shortest path of rotation.
float degree0 = 0.0f;
m_subTracks[i]->GetValue(time, degree0);
degree = PreferShortestRotPath(degree, degree0);
}
m_subTracks[i]->SetValue(time, degree, bDefault);
}
}
else
{
assert(0);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const AZ::Vector2& value, bool bDefault)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value.GetElement(i), bDefault);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const AZ::Vector3& value, bool bDefault)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value.GetElement(i), bDefault);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const AZ::Vector4& value, bool bDefault)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value.GetElement(i), bDefault);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetValue(float time, const AZ::Color& value, bool bDefault)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value.GetElement(i), bDefault);
}
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::OffsetKeyPosition(const Vec3& offset)
{
if (m_nDimensions == 3)
{
for (int i = 0; i < 3; i++)
{
IUiAnimTrack* pSubTrack = m_subTracks[i].get();
// Iterate over all keys.
for (int k = 0, num = pSubTrack->GetNumKeys(); k < num; k++)
{
// Offset each key.
float time = pSubTrack->GetKeyTime(k);
float value = 0;
pSubTrack->GetValue(time, value);
value = value + offset[i];
pSubTrack->SetValue(time, value);
}
}
}
else
{
assert(0);
}
}
//////////////////////////////////////////////////////////////////////////
IUiAnimTrack* UiCompoundSplineTrack::GetSubTrack(int nIndex) const
{
assert(nIndex >= 0 && nIndex < m_nDimensions);
return m_subTracks[nIndex].get();
}
//////////////////////////////////////////////////////////////////////////
AZStd::string UiCompoundSplineTrack::GetSubTrackName(int nIndex) const
{
assert(nIndex >= 0 && nIndex < m_nDimensions);
return m_subTrackNames[nIndex];
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::SetSubTrackName(int nIndex, const char* name)
{
assert(nIndex >= 0 && nIndex < m_nDimensions);
assert(name);
m_subTrackNames[nIndex] = name;
}
//////////////////////////////////////////////////////////////////////////
int UiCompoundSplineTrack::GetNumKeys() const
{
int nKeys = 0;
for (int i = 0; i < m_nDimensions; i++)
{
nKeys += m_subTracks[i]->GetNumKeys();
}
return nKeys;
}
//////////////////////////////////////////////////////////////////////////
bool UiCompoundSplineTrack::HasKeys() const
{
for (int i = 0; i < m_nDimensions; i++)
{
if (m_subTracks[i]->GetNumKeys())
{
return true;
}
}
return false;
}
float UiCompoundSplineTrack::PreferShortestRotPath(float degree, float degree0) const
{
// Assumes the degree is in (-PI, PI).
assert(-181.0f < degree && degree < 181.0f);
float degree00 = degree0;
degree0 = fmod_tpl(degree0, 360.0f);
float n = (degree00 - degree0) / 360.0f;
float degreeAlt;
if (degree >= 0)
{
degreeAlt = degree - 360.0f;
}
else
{
degreeAlt = degree + 360.0f;
}
if (fabs(degreeAlt - degree0) < fabs(degree - degree0))
{
return degreeAlt + n * 360.0f;
}
else
{
return degree + n * 360.0f;
}
}
int UiCompoundSplineTrack::GetSubTrackIndex(int& key) const
{
assert(key >= 0 && key < GetNumKeys());
int count = 0;
for (int i = 0; i < m_nDimensions; i++)
{
if (key < count + m_subTracks[i]->GetNumKeys())
{
key = key - count;
return i;
}
count += m_subTracks[i]->GetNumKeys();
}
return -1;
}
void UiCompoundSplineTrack::RemoveKey(int num)
{
assert(num >= 0 && num < GetNumKeys());
int i = GetSubTrackIndex(num);
assert(i >= 0);
if (i < 0)
{
return;
}
m_subTracks[i]->RemoveKey(num);
}
void UiCompoundSplineTrack::GetKeyInfo(int key, const char*& description, float& duration)
{
static char str[64];
duration = 0;
description = str;
const char* subDesc = NULL;
float time = GetKeyTime(key);
int m = 0;
/// Using the time obtained, combine descriptions from keys of the same time
/// in sub-tracks if any into one compound description.
str[0] = 0;
// A head case
for (m = 0; m < m_subTracks[0]->GetNumKeys(); ++m)
{
if (m_subTracks[0]->GetKeyTime(m) == time)
{
float dummy;
m_subTracks[0]->GetKeyInfo(m, subDesc, dummy);
azstrcat(str, AZ_ARRAY_SIZE(str), subDesc);
break;
}
}
if (m == m_subTracks[0]->GetNumKeys())
{
azstrcat(str, AZ_ARRAY_SIZE(str), m_subTrackNames[0].c_str());
}
// Tail cases
for (int i = 1; i < GetSubTrackCount(); ++i)
{
azstrcat(str, AZ_ARRAY_SIZE(str), ",");
for (m = 0; m < m_subTracks[i]->GetNumKeys(); ++m)
{
if (m_subTracks[i]->GetKeyTime(m) == time)
{
float dummy;
m_subTracks[i]->GetKeyInfo(m, subDesc, dummy);
azstrcat(str, AZ_ARRAY_SIZE(str), subDesc);
break;
}
}
if (m == m_subTracks[i]->GetNumKeys())
{
azstrcat(str, AZ_ARRAY_SIZE(str), m_subTrackNames[i].c_str());
}
}
}
float UiCompoundSplineTrack::GetKeyTime(int index) const
{
assert(index >= 0 && index < GetNumKeys());
int i = GetSubTrackIndex(index);
assert(i >= 0);
if (i < 0)
{
return 0;
}
return m_subTracks[i]->GetKeyTime(index);
}
void UiCompoundSplineTrack::SetKeyTime(int index, float time)
{
assert(index >= 0 && index < GetNumKeys());
int i = GetSubTrackIndex(index);
assert(i >= 0);
if (i < 0)
{
return;
}
m_subTracks[i]->SetKeyTime(index, time);
}
bool UiCompoundSplineTrack::IsKeySelected(int key) const
{
assert(key >= 0 && key < GetNumKeys());
int i = GetSubTrackIndex(key);
assert(i >= 0);
if (i < 0)
{
return false;
}
return m_subTracks[i]->IsKeySelected(key);
}
void UiCompoundSplineTrack::SelectKey(int key, bool select)
{
assert(key >= 0 && key < GetNumKeys());
int i = GetSubTrackIndex(key);
assert(i >= 0);
if (i < 0)
{
return;
}
float keyTime = m_subTracks[i]->GetKeyTime(key);
// In the case of compound tracks, animators want to
// select all keys of the same time in the sub-tracks together.
const float timeEpsilon = 0.001f;
for (int k = 0; k < m_nDimensions; ++k)
{
for (int m = 0; m < m_subTracks[k]->GetNumKeys(); ++m)
{
if (fabs(m_subTracks[k]->GetKeyTime(m) - keyTime) < timeEpsilon)
{
m_subTracks[k]->SelectKey(m, select);
break;
}
}
}
}
int UiCompoundSplineTrack::NextKeyByTime(int key) const
{
assert(key >= 0 && key < GetNumKeys());
float time = GetKeyTime(key);
int count = 0, result = -1;
float timeNext = FLT_MAX;
for (int i = 0; i < GetSubTrackCount(); ++i)
{
for (int k = 0; k < m_subTracks[i]->GetNumKeys(); ++k)
{
float t = m_subTracks[i]->GetKeyTime(k);
if (t > time)
{
if (t < timeNext)
{
timeNext = t;
result = count + k;
}
break;
}
}
count += m_subTracks[i]->GetNumKeys();
}
return result;
}
//////////////////////////////////////////////////////////////////////////
void UiCompoundSplineTrack::Reflect(AZ::SerializeContext* serializeContext)
{
serializeContext->Class<UiCompoundSplineTrack>()
->Version(1)
->Field("Flags", &UiCompoundSplineTrack::m_flags)
->Field("ParamType", &UiCompoundSplineTrack::m_nParamType)
->Field("ParamData", &UiCompoundSplineTrack::m_componentParamData)
->Field("NumSubTracks", &UiCompoundSplineTrack::m_nDimensions)
->Field("SubTracks", &UiCompoundSplineTrack::m_subTracks)
->Field("SubTrackNames", &UiCompoundSplineTrack::m_subTrackNames);
}