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/Maestro/Code/Source/Cinematics/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 <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Transform.h>
#include "CompoundSplineTrack.h"
#include "AnimSplineTrack.h"
#include "Maestro/Types/AnimParamType.h"
#include "Maestro/Types/AnimValueType.h"
CCompoundSplineTrack::CCompoundSplineTrack(int nDims, AnimValueType inValueType, CAnimParamType subTrackParamTypes[MAX_SUBTRACKS], bool expanded)
: m_refCount(0)
, m_expanded(expanded)
{
assert(nDims > 0 && nDims <= MAX_SUBTRACKS);
m_node = nullptr;
m_nDimensions = nDims;
m_valueType = inValueType;
m_nParamType = AnimParamType::Invalid;
m_flags = 0;
m_subTracks.resize(MAX_SUBTRACKS);
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i].reset(aznew C2DSplineTrack());
m_subTracks[i]->SetParameterType(subTrackParamTypes[i]);
if (inValueType == AnimValueType::RGB)
{
m_subTracks[i]->SetKeyValueRange(0.0f, 255.f);
}
}
m_subTrackNames.resize(MAX_SUBTRACKS);
m_subTrackNames[0] = "X";
m_subTrackNames[1] = "Y";
m_subTrackNames[2] = "Z";
m_subTrackNames[3] = "W";
#ifdef MOVIESYSTEM_SUPPORT_EDITING
m_bCustomColorSet = false;
#endif
}
//////////////////////////////////////////////////////////////////////////
// Need default constructor for AZ Serialization
CCompoundSplineTrack::CCompoundSplineTrack()
: m_refCount(0)
, m_nDimensions(0)
, m_valueType(AnimValueType::Float)
#ifdef MOVIESYSTEM_SUPPORT_EDITING
, m_bCustomColorSet(false)
#endif
, m_expanded(false)
{
}
void CCompoundSplineTrack::SetNode(IAnimNode* node)
{
m_node = node;
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetNode(node);
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::SetTimeRange(const Range& timeRange)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetTimeRange(timeRange);
}
}
//////////////////////////////////////////////////////////////////////////
/// @deprecated Serialization for Sequence data in Component Entity Sequences now occurs through AZ::SerializeContext and the Sequence Component
bool CCompoundSplineTrack::Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks /*=true */)
{
#ifdef MOVIESYSTEM_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);
}
xmlNode->getAttr("Id", m_id);
}
else
{
int flags = GetFlags();
xmlNode->setAttr("Flags", flags);
xmlNode->setAttr("HasCustomColor", m_bCustomColorSet);
if (m_bCustomColorSet)
{
xmlNode->setAttr("CustomColor", m_customColor.pack_abgr8888());
}
xmlNode->setAttr("Id", m_id);
}
#endif
for (int i = 0; i < m_nDimensions; i++)
{
XmlNodeRef subTrackNode;
if (bLoading)
{
subTrackNode = xmlNode->getChild(i);
}
else
{
subTrackNode = xmlNode->newChild("NewSubTrack");
}
m_subTracks[i]->Serialize(subTrackNode, bLoading, bLoadEmptyTracks);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CCompoundSplineTrack::SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected /*=false*/, float fTimeOffset /*=0*/)
{
for (int i = 0; i < m_nDimensions; i++)
{
XmlNodeRef subTrackNode;
if (bLoading)
{
subTrackNode = xmlNode->getChild(i);
}
else
{
subTrackNode = xmlNode->newChild("NewSubTrack");
}
m_subTracks[i]->SerializeSelection(subTrackNode, bLoading, bCopySelected, fTimeOffset);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::GetValue(float time, float& value, bool applyMultiplier)
{
for (int i = 0; i < 1 && i < m_nDimensions; i++)
{
m_subTracks[i]->GetValue(time, value, applyMultiplier);
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::GetValue(float time, Vec3& value, bool applyMultiplier)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value[i];
m_subTracks[i]->GetValue(time, v, applyMultiplier);
value[i] = v;
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::GetValue(float time, Vec4& value, bool applyMultiplier)
{
for (int i = 0; i < m_nDimensions; i++)
{
float v = value[i];
m_subTracks[i]->GetValue(time, v, applyMultiplier);
value[i] = v;
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::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 CCompoundSplineTrack::SetValue(float time, const float& value, bool bDefault, bool applyMultiplier)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value, bDefault, applyMultiplier);
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::SetValue(float time, const Vec3& value, bool bDefault, bool applyMultiplier)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value[i], bDefault, applyMultiplier);
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::SetValue(float time, const Vec4& value, bool bDefault, bool applyMultiplier)
{
for (int i = 0; i < m_nDimensions; i++)
{
m_subTracks[i]->SetValue(time, value[i], bDefault, applyMultiplier);
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::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 CCompoundSplineTrack::OffsetKeyPosition(const Vec3& offset)
{
if (m_nDimensions == 3)
{
for (int i = 0; i < 3; i++)
{
IAnimTrack* 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);
}
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::UpdateKeyDataAfterParentChanged(const AZ::Transform& oldParentWorldTM, const AZ::Transform& newParentWorldTM)
{
// Only update the position tracks
if (m_nParamType.GetType() != AnimParamType::Position)
{
return;
}
AZ_Assert(m_nDimensions == 3, "Expected 3 dimensions, position, rotation or scale.");
struct KeyValues
{
KeyValues(int i, float t, float v) : index(i), time(t), value(v) {};
int index;
float time;
float value;
};
// Don't actually set the key data until we are done calculating all the new values.
// In the case where not all 3 tracks have key data, data from other keys may be referenced
// and used. So we don't want to muck with those other keys until we are done transforming all of
// the key data.
AZStd::vector<KeyValues> newKeyValues;
// Collect all times that have key data on any track.
for (int subTrackIndex = 0; subTrackIndex < 3; subTrackIndex++)
{
IAnimTrack* subTrack = m_subTracks[subTrackIndex].get();
for (int k = 0, num = subTrack->GetNumKeys(); k < num; k++)
{
// If this key time is not already in the list, add it.
float time = subTrack->GetKeyTime(k);
// Create a 3 float vector with values from the 3 tracks.
AZ::Vector3 vector;
for (int i = 0; i < 3; i++)
{
float value = 0;
m_subTracks[i]->GetValue(time, value);
vector.SetElement(i, value);
}
// Use the old parent world transform to get the current key data into world space.
AZ::Vector3 worldPosition = oldParentWorldTM.GetTranslation() + vector;
// Get the world location into local space relative to the new parent.
vector = worldPosition - newParentWorldTM.GetTranslation();
// Store the new key data in a list to be set to keys later.
newKeyValues.push_back(KeyValues(subTrackIndex, time, vector.GetElement(subTrackIndex)));
}
}
// Set key data for each time gathered from the keys.
for (auto valuePair : newKeyValues)
{
m_subTracks[valuePair.index]->SetValue(valuePair.time, valuePair.value);
}
}
//////////////////////////////////////////////////////////////////////////
IAnimTrack* CCompoundSplineTrack::GetSubTrack(int nIndex) const
{
assert(nIndex >= 0 && nIndex < m_nDimensions);
return m_subTracks[nIndex].get();
}
//////////////////////////////////////////////////////////////////////////
AZStd::string CCompoundSplineTrack::GetSubTrackName(int nIndex) const
{
assert(nIndex >= 0 && nIndex < m_nDimensions);
return m_subTrackNames[nIndex];
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::SetSubTrackName(int nIndex, const char* name)
{
assert(nIndex >= 0 && nIndex < m_nDimensions);
assert(name);
m_subTrackNames[nIndex] = name;
}
//////////////////////////////////////////////////////////////////////////
int CCompoundSplineTrack::GetNumKeys() const
{
int nKeys = 0;
for (int i = 0; i < m_nDimensions; i++)
{
nKeys += m_subTracks[i]->GetNumKeys();
}
return nKeys;
}
//////////////////////////////////////////////////////////////////////////
bool CCompoundSplineTrack::HasKeys() const
{
for (int i = 0; i < m_nDimensions; i++)
{
if (m_subTracks[i]->GetNumKeys())
{
return true;
}
}
return false;
}
float CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::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 CCompoundSplineTrack::SetExpanded(bool expanded)
{
m_expanded = expanded;
}
//////////////////////////////////////////////////////////////////////////
bool CCompoundSplineTrack::GetExpanded() const
{
return m_expanded;
}
//////////////////////////////////////////////////////////////////////////
unsigned int CCompoundSplineTrack::GetId() const
{
return m_id;
}
//////////////////////////////////////////////////////////////////////////
void CCompoundSplineTrack::SetId(unsigned int id)
{
m_id = id;
}
//////////////////////////////////////////////////////////////////////////
static bool CompoundSplineTrackVersionConverter(
AZ::SerializeContext& serializeContext,
AZ::SerializeContext::DataElementNode& rootElement)
{
if (rootElement.GetVersion() < 4)
{
rootElement.AddElement(serializeContext, "BaseClass1", azrtti_typeid<IAnimTrack>());
}
return true;
}
void CCompoundSplineTrack::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CCompoundSplineTrack, IAnimTrack>()
->Version(4, &CompoundSplineTrackVersionConverter)
->Field("Flags", &CCompoundSplineTrack::m_flags)
->Field("ParamType", &CCompoundSplineTrack::m_nParamType)
->Field("NumSubTracks", &CCompoundSplineTrack::m_nDimensions)
->Field("SubTracks", &CCompoundSplineTrack::m_subTracks)
->Field("SubTrackNames", &CCompoundSplineTrack::m_subTrackNames)
->Field("ValueType", &CCompoundSplineTrack::m_valueType)
->Field("Expanded", &CCompoundSplineTrack::m_expanded)
->Field("Id", &CCompoundSplineTrack::m_id);
}
}