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/AnimSplineTrack.h

662 lines
19 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
*
*/
#ifndef CRYINCLUDE_CRYMOVIE_ANIMSPLINETRACK_H
#define CRYINCLUDE_CRYMOVIE_ANIMSPLINETRACK_H
#pragma once
#include "IMovieSystem.h"
#include "TCBSpline.h"
#include "2DSpline.h"
#define MIN_TIME_PRECISION 0.01f
#define MIN_VALUE_RANGE 1.0f // prevents fill sliders from being inoperable on the first key frame
/*!
Templated class that used as a base for all Tcb spline tracks.
*/
template <class ValueType>
class TAnimSplineTrack
: public IAnimTrack
{
public:
AZ_CLASS_ALLOCATOR(TAnimSplineTrack, AZ::SystemAllocator, 0);
AZ_RTTI((TAnimSplineTrack, "{6D72D5F6-61A7-43D4-9104-8F7DCCC19E10}", Vec2), IAnimTrack)
TAnimSplineTrack()
: m_refCount(0)
{
AllocSpline();
m_flags = 0;
m_bCustomColorSet = false;
m_fMinKeyValue = 0.0f;
m_fMaxKeyValue = 0.0f;
m_node = nullptr;
m_trackMultiplier = 1.0f;
}
~TAnimSplineTrack()
{
m_spline.reset();
}
//////////////////////////////////////////////////////////////////////////
// for intrusive_ptr support
void add_ref() override;
void release() override;
//////////////////////////////////////////////////////////////////////////
virtual int GetSubTrackCount() const { return 0; };
virtual IAnimTrack* GetSubTrack([[maybe_unused]] int nIndex) const { return 0; };
AZStd::string GetSubTrackName([[maybe_unused]] int nIndex) const { return AZStd::string(); };
virtual void SetSubTrackName([[maybe_unused]] int nIndex, [[maybe_unused]] const char* name) { assert(0); }
void SetNode(IAnimNode* node) override { m_node = node; }
// Return Animation Node that owns this Track.
IAnimNode* GetNode() override { return m_node; }
virtual const CAnimParamType& GetParameterType() const { return m_nParamType; };
virtual void SetParameterType(CAnimParamType type) { m_nParamType = type; };
virtual void GetKeyValueRange(float& fMin, float& fMax) const { fMin = m_fMinKeyValue; fMax = m_fMaxKeyValue; };
virtual void SetKeyValueRange(float fMin, float fMax){ m_fMinKeyValue = fMin; m_fMaxKeyValue = fMax; };
ISplineInterpolator* GetSpline() const { return m_spline.get(); };
virtual bool IsKeySelected(int key) const
{
if (GetSpline() && GetSpline()->IsKeySelectedAtAnyDimension(key))
{
return true;
}
return false;
}
virtual void SelectKey(int key, bool select)
{
if (GetSpline())
{
GetSpline()->SelectKeyAllDimensions(key, select);
}
}
int GetNumKeys() const
{
return m_spline->num_keys();
}
void SetNumKeys(int numKeys)
{
m_spline->resize(numKeys);
}
bool HasKeys() const
{
return GetNumKeys() != 0;
}
void RemoveKey(int num)
{
if (m_spline && m_spline->num_keys() > num)
{
m_spline->erase(num);
}
else
{
assert(0);
}
}
void GetKey(int index, IKey* key) const
{
assert(index >= 0 && index < GetNumKeys());
assert(key != 0);
typename Spline::key_type& k = m_spline->key(index);
ITcbKey* tcbkey = (ITcbKey*)key;
tcbkey->time = k.time;
tcbkey->flags = k.flags;
tcbkey->tens = k.tens;
tcbkey->cont = k.cont;
tcbkey->bias = k.bias;
tcbkey->easeto = k.easeto;
tcbkey->easefrom = k.easefrom;
tcbkey->SetValue(k.value);
}
void SetKey(int index, IKey* key)
{
assert(index >= 0 && index < GetNumKeys());
assert(key != 0);
typename Spline::key_type& k = m_spline->key(index);
ITcbKey* tcbkey = (ITcbKey*)key;
k.time = tcbkey->time;
k.flags = tcbkey->flags;
k.tens = tcbkey->tens;
k.cont = tcbkey->cont;
k.bias = tcbkey->bias;
k.easeto = tcbkey->easeto;
k.easefrom = tcbkey->easefrom;
tcbkey->GetValue(k.value);
Invalidate();
}
float GetKeyTime(int index) const
{
assert(index >= 0 && index < GetNumKeys());
return m_spline->time(index);
}
void SetKeyTime(int index, float time)
{
assert(index >= 0 && index < GetNumKeys());
m_spline->SetKeyTime(index, time);
Invalidate();
}
int GetKeyFlags(int index)
{
assert(index >= 0 && index < GetNumKeys());
return m_spline->key(index).flags;
}
void SetKeyFlags(int index, int flags)
{
assert(index >= 0 && index < GetNumKeys());
m_spline->key(index).flags = flags;
}
virtual EAnimCurveType GetCurveType() { assert(0); return eAnimCurveType_Unknown; }
virtual AnimValueType GetValueType() { assert(0); return static_cast<AnimValueType>(0xFFFFFFFF); }
virtual void GetValue(float time, float& value, bool applyMultiplier = false) { assert(0); }
virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec3& value, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }
virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Vec4& value, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }
virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Quat& value) { assert(0); }
virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] bool& value) { assert(0); }
virtual void GetValue([[maybe_unused]] float time, [[maybe_unused]] Maestro::AssetBlends<AZ::Data::AssetData>& value) { assert(0); }
virtual void SetValue(float time, const float& value, bool bDefault = false, bool applyMultiplier = false) { assert(0); }
virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec3& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }
virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Vec4& value, [[maybe_unused]] bool bDefault = false, [[maybe_unused]] bool applyMultiplier = false) { assert(0); }
virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Quat& value, [[maybe_unused]] bool bDefault = false) { assert(0); }
virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const bool& value, [[maybe_unused]] bool bDefault = false) { assert(0); }
virtual void SetValue([[maybe_unused]] float time, [[maybe_unused]] const Maestro::AssetBlends<AZ::Data::AssetData>& value, [[maybe_unused]] bool bDefault = false) { assert(0); }
virtual void OffsetKeyPosition([[maybe_unused]] const Vec3& value) { assert(0); };
virtual void UpdateKeyDataAfterParentChanged([[maybe_unused]] const AZ::Transform& oldParentWorldTM, [[maybe_unused]] const AZ::Transform& newParentWorldTM) { assert(0); };
bool Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks);
bool SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected, float fTimeOffset);
void GetKeyInfo(int key, const char*& description, float& duration)
{
description = 0;
duration = 0;
}
//! Sort keys in track (after time of keys was modified).
void SortKeys()
{
m_spline->sort_keys();
};
//! Get track flags.
int GetFlags() { return m_flags; };
//! Check if track is masked by mask
virtual bool IsMasked([[maybe_unused]] const uint32 mask) const { return false; }
//! Set track flags.
void SetFlags(int flags)
{
m_flags = flags;
if (m_flags & eAnimTrackFlags_Loop)
{
m_spline->ORT(Spline::ORT_LOOP);
}
else if (m_flags & eAnimTrackFlags_Cycle)
{
m_spline->ORT(Spline::ORT_CYCLE);
}
else
{
m_spline->ORT(Spline::ORT_CONSTANT);
}
}
void Invalidate()
{
m_spline->flag_set(Spline::MODIFIED);
};
void SetTimeRange(const Range& timeRange)
{
m_spline->SetRange(timeRange.start, timeRange.end);
}
int FindKey(float time)
{
// Find key with given time.
int num = m_spline->num_keys();
for (int i = 0; i < num; i++)
{
float keyt = m_spline->key(i).time;
if (fabs(keyt - time) < MIN_TIME_PRECISION)
{
return i;
}
}
return -1;
}
//! Create key at given time, and return its index.
int CreateKey(float time)
{
ValueType value;
int nkey = GetNumKeys();
if (nkey > 0)
{
GetValue(time, value);
}
else
{
value = m_defaultValue;
}
typename Spline::ValueType tmp;
m_spline->ToValueType(value, tmp);
return m_spline->InsertKey(time, tmp);
}
int CloneKey(int srcKey)
{
return CopyKey(this, srcKey);
}
int CopyKey(IAnimTrack* pFromTrack, int nFromKey)
{
ITcbKey key;
pFromTrack->GetKey(nFromKey, &key);
int nkey = GetNumKeys();
SetNumKeys(nkey + 1);
SetKey(nkey, &key);
return nkey;
}
//! Get key at given time,
//! If key not exist adds key at this time.
void SetKeyAtTime(float time, IKey* key)
{
assert(key != 0);
key->time = time;
bool found = false;
// Find key with given time.
for (int i = 0; i < m_spline->num_keys(); i++)
{
float keyt = m_spline->key(i).time;
if (fabs(keyt - time) < MIN_TIME_PRECISION)
{
key->flags = m_spline->key(i).flags; // Reserve the flag value.
SetKey(i, key);
found = true;
break;
}
//if (keyt > time)
//break;
}
if (!found)
{
// Key with this time not found.
// Create a new one.
int keyIndex = CreateKey(time);
// Reserve the flag value.
key->flags = m_spline->key(keyIndex).flags; // Reserve the flag value.
SetKey(keyIndex, key);
}
}
virtual void SetDefaultValue(const ValueType& value)
{
m_defaultValue = value;
}
virtual ColorB GetCustomColor() const
{ return m_customColor; }
virtual void SetCustomColor(ColorB color)
{
m_customColor = color;
m_bCustomColorSet = true;
}
virtual bool HasCustomColor() const
{ return m_bCustomColorSet; }
virtual void ClearCustomColor()
{ m_bCustomColorSet = false; }
void SetMultiplier(float trackMultiplier) override
{
m_trackMultiplier = trackMultiplier;
}
void SetExpanded([[maybe_unused]] bool expanded)
{
AZ_Assert(false, "Not expected to be used.");
}
bool GetExpanded() const
{
return false;
}
unsigned int GetId() const override
{
return m_id;
}
void SetId(unsigned int id) override
{
m_id = id;
}
static void Reflect([[maybe_unused]] AZ::ReflectContext* context) {}
protected:
void UpdateTrackValueRange(float newValue)
{
m_fMinKeyValue = (newValue < m_fMinKeyValue) ? newValue : m_fMinKeyValue;
m_fMaxKeyValue = (newValue > m_fMaxKeyValue) ? newValue : m_fMaxKeyValue;
if ((m_fMaxKeyValue - m_fMinKeyValue) < MIN_VALUE_RANGE)
{
// prevents fill sliders from being inoperable when min and max are identical (or close to it)
m_fMaxKeyValue = (m_fMinKeyValue + MIN_VALUE_RANGE);
}
};
private:
//! Spawns new instance of Tcb spline.
void AllocSpline()
{
m_spline = aznew spline::TrackSplineInterpolator<ValueType>;
}
int m_refCount;
typedef spline::TrackSplineInterpolator<ValueType> Spline;
AZStd::intrusive_ptr<Spline> m_spline;
ValueType m_defaultValue;
//! Keys of float track.
int m_flags;
CAnimParamType m_nParamType;
ColorB m_customColor;
bool m_bCustomColorSet;
float m_fMinKeyValue;
float m_fMaxKeyValue;
IAnimNode* m_node;
float m_trackMultiplier;
unsigned int m_id = 0;
static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) {};
};
//////////////////////////////////////////////////////////////////////////
template <class T>
inline void TAnimSplineTrack<T>::add_ref()
{
++m_refCount;
}
//////////////////////////////////////////////////////////////////////////
template <class T>
inline void TAnimSplineTrack<T>::release()
{
if (--m_refCount <= 0)
{
delete this;
}
}
/// @deprecated Serialization for Sequence data in Component Entity Sequences now occurs through AZ::SerializeContext and the Sequence Component
template <class T>
inline bool TAnimSplineTrack<T>::Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks)
{
if (bLoading)
{
int num = xmlNode->getChildCount();
int flags = m_flags;
xmlNode->getAttr("Flags", flags);
xmlNode->getAttr("defaultValue", m_defaultValue);
SetFlags(flags);
xmlNode->getAttr("HasCustomColor", m_bCustomColorSet);
if (m_bCustomColorSet)
{
unsigned int abgr;
xmlNode->getAttr("CustomColor", abgr);
m_customColor = ColorB(abgr);
}
T value;
SetNumKeys(num);
for (int i = 0; i < num; i++)
{
ITcbKey key; // Must be inside loop.
XmlNodeRef keyNode = xmlNode->getChild(i);
keyNode->getAttr("time", key.time);
if (keyNode->getAttr("value", value))
{
key.SetValue(value);
}
keyNode->getAttr("tens", key.tens);
keyNode->getAttr("cont", key.cont);
keyNode->getAttr("bias", key.bias);
keyNode->getAttr("easeto", key.easeto);
keyNode->getAttr("easefrom", key.easefrom);
keyNode->getAttr("flags", key.flags);
SetKey(i, &key);
// In-/Out-tangent
keyNode->getAttr("ds", m_spline->key(i).ds);
keyNode->getAttr("dd", m_spline->key(i).dd);
}
xmlNode->getAttr("Id", m_id);
if ((!num) && (!bLoadEmptyTracks))
{
return false;
}
}
else
{
int num = GetNumKeys();
xmlNode->setAttr("Flags", GetFlags());
xmlNode->setAttr("defaultValue", m_defaultValue);
xmlNode->setAttr("HasCustomColor", m_bCustomColorSet);
if (m_bCustomColorSet)
{
xmlNode->setAttr("CustomColor", m_customColor.pack_abgr8888());
}
ITcbKey key;
T value;
for (int i = 0; i < num; i++)
{
GetKey(i, &key);
XmlNodeRef keyNode = xmlNode->newChild("Key");
keyNode->setAttr("time", key.time);
key.GetValue(value);
keyNode->setAttr("value", value);
if (key.tens != 0)
{
keyNode->setAttr("tens", key.tens);
}
if (key.cont != 0)
{
keyNode->setAttr("cont", key.cont);
}
if (key.bias != 0)
{
keyNode->setAttr("bias", key.bias);
}
if (key.easeto != 0)
{
keyNode->setAttr("easeto", key.easeto);
}
if (key.easefrom != 0)
{
keyNode->setAttr("easefrom", key.easefrom);
}
int flags = key.flags;
// Just save the in/out mask part. Others are for editing convenience.
flags &= (SPLINE_KEY_TANGENT_IN_MASK | SPLINE_KEY_TANGENT_OUT_MASK);
if (flags != 0)
{
keyNode->setAttr("flags", flags);
}
// We also have to save in-/out-tangents, because TCB infos are not used for custom tangent keys.
keyNode->setAttr("ds", m_spline->key(i).ds);
keyNode->setAttr("dd", m_spline->key(i).dd);
}
xmlNode->setAttr("Id", m_id);
}
return true;
}
template <class T>
inline bool TAnimSplineTrack<T>::SerializeSelection(XmlNodeRef& xmlNode, bool bLoading, bool bCopySelected, float fTimeOffset)
{
if (bLoading)
{
int numCur = GetNumKeys();
int num = xmlNode->getChildCount();
int type;
xmlNode->getAttr("TrackType", type);
if (type != GetCurveType())
{
return false;
}
T value;
SetNumKeys(num + numCur);
for (int i = 0; i < num; i++)
{
ITcbKey key; // Must be inside loop.
XmlNodeRef keyNode = xmlNode->getChild(i);
keyNode->getAttr("time", key.time);
key.time += fTimeOffset;
if (keyNode->getAttr("value", value))
{
key.SetValue(value);
}
keyNode->getAttr("tens", key.tens);
keyNode->getAttr("cont", key.cont);
keyNode->getAttr("bias", key.bias);
keyNode->getAttr("easeto", key.easeto);
keyNode->getAttr("easefrom", key.easefrom);
keyNode->getAttr("flags", key.flags);
SetKey(i + numCur, &key);
if (bCopySelected)
{
SelectKey(i + numCur, true);
}
// In-/Out-tangent
keyNode->getAttr("ds", m_spline->key(i + numCur).ds);
keyNode->getAttr("dd", m_spline->key(i + numCur).dd);
}
SortKeys();
}
else
{
int num = GetNumKeys();
xmlNode->setAttr("TrackType", GetCurveType());
ITcbKey key;
T value;
for (int i = 0; i < num; i++)
{
GetKey(i, &key);
if (!bCopySelected || IsKeySelected(i))
{
XmlNodeRef keyNode = xmlNode->newChild("Key");
keyNode->setAttr("time", key.time);
key.GetValue(value);
keyNode->setAttr("value", value);
if (key.tens != 0)
{
keyNode->setAttr("tens", key.tens);
}
if (key.cont != 0)
{
keyNode->setAttr("cont", key.cont);
}
if (key.bias != 0)
{
keyNode->setAttr("bias", key.bias);
}
if (key.easeto != 0)
{
keyNode->setAttr("easeto", key.easeto);
}
if (key.easefrom != 0)
{
keyNode->setAttr("easefrom", key.easefrom);
}
int flags = key.flags;
// Just save the in/out mask part. Others are for editing convenience.
flags &= (SPLINE_KEY_TANGENT_IN_MASK | SPLINE_KEY_TANGENT_OUT_MASK);
if (flags != 0)
{
keyNode->setAttr("flags", flags);
}
// We also have to save in-/out-tangents, because TCB infos are not used for custom tangent keys.
keyNode->setAttr("ds", m_spline->key(i).ds);
keyNode->setAttr("dd", m_spline->key(i).dd);
}
}
}
return true;
}
// This is the current main.
#include "AnimSplineTrack_Vec2Specialization.h"
typedef TAnimSplineTrack<Vec2> C2DSplineTrack;
#endif // CRYINCLUDE_CRYMOVIE_ANIMSPLINETRACK_H