/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Description : 'Vec2' explicit specialization of the class template // TAnimSplineTrack // Notice : Should be included in AnimSplineTrack h only #ifndef CRYINCLUDE_CRYMOVIE_ANIMSPLINETRACK_VEC2SPECIALIZATION_H #define CRYINCLUDE_CRYMOVIE_ANIMSPLINETRACK_VEC2SPECIALIZATION_H #pragma once #include <CryCommonReflection.h> #include <AzCore/Serialization/EditContext.h> template <> inline TAnimSplineTrack<Vec2>::TAnimSplineTrack() : m_refCount(0) { AllocSpline(); m_flags = 0; m_defaultValue = Vec2(0, 0); m_fMinKeyValue = 0.0f; m_fMaxKeyValue = 0.0f; m_bCustomColorSet = false; m_node = nullptr; m_trackMultiplier = 1.0f; } template <> inline void TAnimSplineTrack<Vec2>::GetValue(float time, float& value, bool applyMultiplier) { if (GetNumKeys() == 0) { value = m_defaultValue.y; } else { Spline::ValueType tmp; m_spline->Interpolate(time, tmp); value = tmp[0]; } if (applyMultiplier && m_trackMultiplier != 1.0f) { value /= m_trackMultiplier; } } template <> inline EAnimCurveType TAnimSplineTrack<Vec2>::GetCurveType() { return eAnimCurveType_BezierFloat; } template <> inline AnimValueType TAnimSplineTrack<Vec2>::GetValueType() { return kAnimValueDefault; } template <> inline void TAnimSplineTrack<Vec2>::SetValue(float time, const float& value, bool bDefault, bool applyMultiplier) { if (!bDefault) { I2DBezierKey key; if (applyMultiplier && m_trackMultiplier != 1.0f) { key.value = Vec2(time, value * m_trackMultiplier); } else { key.value = Vec2(time, value); } SetKeyAtTime(time, &key); } else { if (applyMultiplier && m_trackMultiplier != 1.0f) { m_defaultValue = Vec2(time, value * m_trackMultiplier); } else { m_defaultValue = Vec2(time, value); } } } template <> inline void TAnimSplineTrack<Vec2>::GetKey(int index, IKey* key) const { assert(index >= 0 && index < GetNumKeys()); assert(key != 0); Spline::key_type& k = m_spline->key(index); I2DBezierKey* bezierkey = (I2DBezierKey*)key; bezierkey->time = k.time; bezierkey->flags = k.flags; bezierkey->value = k.value; } template <> inline void TAnimSplineTrack<Vec2>::SetKey(int index, IKey* key) { assert(index >= 0 && index < GetNumKeys()); assert(key != 0); Spline::key_type& k = m_spline->key(index); I2DBezierKey* bezierkey = (I2DBezierKey*)key; k.time = bezierkey->time; k.flags = bezierkey->flags; k.value = bezierkey->value; UpdateTrackValueRange(k.value.y); Invalidate(); } //! Create key at given time, and return its index. template <> inline int TAnimSplineTrack<Vec2>::CreateKey(float time) { float value; int nkey = GetNumKeys(); if (nkey > 0) { GetValue(time, value); } else { value = m_defaultValue.y; } UpdateTrackValueRange(value); Spline::ValueType tmp; tmp[0] = value; tmp[1] = 0; return m_spline->InsertKey(time, tmp); } template <> inline int TAnimSplineTrack<Vec2>::CopyKey(IAnimTrack* pFromTrack, int nFromKey) { // This small time offset is applied to prevent the generation of singular tangents. float timeOffset = 0.01f; I2DBezierKey key; pFromTrack->GetKey(nFromKey, &key); float t = key.time + timeOffset; int newIndex = CreateKey(t); key.time = key.value.x = t; SetKey(newIndex, &key); return newIndex; } /// @deprecated Serialization for Sequence data in Component Entity Sequences now occurs through AZ::SerializeContext and the Sequence Component template <> inline bool TAnimSplineTrack<Vec2>::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); } SetNumKeys(num); for (int i = 0; i < num; i++) { I2DBezierKey key; // Must be inside loop. XmlNodeRef keyNode = xmlNode->getChild(i); if (!keyNode->getAttr("time", key.time)) { CryLog("[CRYMOVIE:TAnimSplineTrack<Vec2>::Serialize]Ill formed legacy track:missing time information."); return false; } if (!keyNode->getAttr("value", key.value)) { CryLog("[CRYMOVIE:TAnimSplineTrack<Vec2>::Serialize]Ill formed legacy track:missing value information."); return false; } //assert(key.time == key.value.x); keyNode->getAttr("flags", key.flags); SetKey(i, &key); // In-/Out-tangent if (!keyNode->getAttr("ds", m_spline->key(i).ds)) { CryLog("[CRYMOVIE:TAnimSplineTrack<Vec2>::Serialize]Ill formed legacy track:missing ds spline information."); return false; } if (!keyNode->getAttr("dd", m_spline->key(i).dd)) { CryLog("[CRYMOVIE:TAnimSplineTrack<Vec2>::Serialize]Ill formed legacy track:dd spline information."); return false; } // now that tangents are loaded, compute the relative angle and size for later unified Tangent manipulations m_spline->key(i).ComputeThetaAndScale(); } 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()); } I2DBezierKey key; for (int i = 0; i < num; i++) { GetKey(i, &key); XmlNodeRef keyNode = xmlNode->newChild("Key"); assert(key.time == key.value.x); keyNode->setAttr("time", key.time); keyNode->setAttr("value", key.value); int flags = key.flags; // Just save the in/out/unify mask part. Others are for editing convenience. flags &= (SPLINE_KEY_TANGENT_IN_MASK | SPLINE_KEY_TANGENT_OUT_MASK | SPLINE_KEY_TANGENT_UNIFY_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 <> inline bool TAnimSplineTrack<Vec2>::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; } SetNumKeys(num + numCur); for (int i = 0; i < num; i++) { I2DBezierKey key; // Must be inside loop. XmlNodeRef keyNode = xmlNode->getChild(i); keyNode->getAttr("time", key.time); keyNode->getAttr("value", key.value); assert(key.time == key.value.x); key.time += fTimeOffset; key.value.x += fTimeOffset; 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()); I2DBezierKey key; for (int i = 0; i < num; i++) { GetKey(i, &key); assert(key.time == key.value.x); if (!bCopySelected || IsKeySelected(i)) { XmlNodeRef keyNode = xmlNode->newChild("Key"); keyNode->setAttr("time", key.time); keyNode->setAttr("value", key.value); 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; } ////////////////////////////////////////////////////////////////////////// template<> inline void TAnimSplineTrack<Vec2>::GetKeyInfo(int index, const char*& description, float& duration) { duration = 0; static char str[64]; description = str; assert(index >= 0 && index < GetNumKeys()); Spline::key_type& k = m_spline->key(index); sprintf_s(str, "%.2f", k.value.y); } using BezierSplineVec2 = spline::BezierSpline<Vec2, spline::SplineKeyEx<Vec2>>; using TSplineBezierBasisVec2 = spline::TSpline<spline::SplineKeyEx<Vec2>, spline::BezierBasis>; namespace AZ { AZ_TYPE_INFO_SPECIALIZE(spline::TrackSplineInterpolator<Vec2>, "{173AC8F0-FD63-4583-8D38-F43FE59F2209}"); AZ_TYPE_INFO_SPECIALIZE(spline::SplineKeyEx<Vec2>, "{96BCA307-A4D5-43A0-9985-08A29BCCCB30}"); AZ_TYPE_INFO_SPECIALIZE(BezierSplineVec2, "{EE318F13-A608-4047-85B3-3D40745A19C7}"); AZ_TYPE_INFO_SPECIALIZE(TSplineBezierBasisVec2, "{B638C840-C1D7-483A-B04E-B22DA539DB8D}"); } namespace spline { ////////////////////////////////////////////////////////////////////////// template <> inline void TSplineBezierBasisVec2::Reflect(AZ::SerializeContext* serializeContext) { serializeContext->Class<TSplineBezierBasisVec2>() ->Version(1) ->Field("Keys", &BezierSplineVec2::m_keys); } ////////////////////////////////////////////////////////////////////////// template <> inline void BezierSplineVec2::Reflect(AZ::SerializeContext* serializeContext) { TSplineBezierBasisVec2::Reflect(serializeContext); serializeContext->Class<BezierSplineVec2, TSplineBezierBasisVec2>() ->Version(1); } } ////////////////////////////////////////////////////////////////////////// // When TAnimSplineTrack<Vec2> is deserialized, a spline instance // is first created in the TUiAnimSplineTrack<Vec2> constructor (via AllocSpline()), // then the pointer is overwritten when "Spline" field is deserialized. // To prevent a memory leak, m_spline is now an intrusive pointer, so that if/when // the "Spline" field is deserialized, the old object will be deleted. template<> inline bool TAnimSplineTrack<Vec2>::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { bool result = true; if (classElement.GetVersion() == 1) { bool converted = false; int splineElementIdx = classElement.FindElement(AZ_CRC("Spline", 0x35f655e9)); if (splineElementIdx != -1) { // Find & copy the raw pointer node AZ::SerializeContext::DataElementNode& splinePtrNodeRef = classElement.GetSubElement(splineElementIdx); AZ::SerializeContext::DataElementNode splinePtrNodeCopy = splinePtrNodeRef; // Reset the node, then convert it to an intrusive pointer splinePtrNodeRef = AZ::SerializeContext::DataElementNode(); if (splinePtrNodeRef.Convert<AZStd::intrusive_ptr<spline::TrackSplineInterpolator<Vec2>>>(context, "Spline")) { // Use the standard name used with the smart pointers serialization // (smart pointers are serialized as containers with one element); // Set the intrusive pointer to the raw pointer value splinePtrNodeCopy.SetName(AZ::SerializeContext::IDataContainer::GetDefaultElementName()); splinePtrNodeRef.AddElement(splinePtrNodeCopy); converted = true; } } // Did not convert. Discard unknown versions if failed to convert, and hope for the best AZ_Assert(converted, "Failed to convert TUiAnimSplineTrack<Vec2> version %d to the current version", classElement.GetVersion()); result = converted; } return result; } ////////////////////////////////////////////////////////////////////////// template<> inline void TAnimSplineTrack<Vec2>::Reflect(AZ::SerializeContext* serializeContext) { spline::SplineKey<Vec2>::Reflect(serializeContext); spline::SplineKeyEx<Vec2>::Reflect(serializeContext); spline::TrackSplineInterpolator<Vec2>::Reflect(serializeContext); BezierSplineVec2::Reflect(serializeContext); serializeContext->Class<TAnimSplineTrack<Vec2> >() ->Version(4, &TAnimSplineTrack<Vec2>::VersionConverter) ->Field("Flags", &TAnimSplineTrack<Vec2>::m_flags) ->Field("DefaultValue", &TAnimSplineTrack<Vec2>::m_defaultValue) ->Field("ParamType", &TAnimSplineTrack<Vec2>::m_nParamType) ->Field("Spline", &TAnimSplineTrack<Vec2>::m_spline) ->Field("Id", &TAnimSplineTrack<Vec2>::m_id); AZ::EditContext* ec = serializeContext->GetEditContext(); // Preventing the default value from being pushed to slice to keep it from dirtying the slice when updated internally if (ec) { ec->Class<TAnimSplineTrack<Vec2>>("TAnimSplineTrack Vec2", "Specialization track for Vec2 AnimSpline")-> DataElement(AZ::Edit::UIHandlers::Vector2, &TAnimSplineTrack<Vec2>::m_defaultValue, "DefaultValue", "")-> Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)-> Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushable); } } #endif // CRYINCLUDE_CRYMOVIE_ANIMSPLINETRACK_VEC2SPECIALIZATION_H