// // AnimationCurve.h // cocos2d_libs // // Created by 徐俊杰 on 2020/5/15. // #ifndef AnimationCurve_h #define AnimationCurve_h #include "rparticle/Macros/RParticleMacros.h" #include "rparticle/Utilities/dynamic_array.h" //#include "SerializeUtility.h" #include "rparticle/Math/FloatConversion.h" //#include "Runtime/Math/Vector3.h" //#include "Runtime/Math/Quaternion.h" //#include "Runtime/Modules/ExportModules.h" //#include "rparticle/Serialize/TransferFunctions/SerializeTransfer.h" #include "rparticle/Serialize/SerializeUtility.h" NS_RRP_BEGIN enum { kDefaultWrapMode = 0, kClamp = 1 << 0, kRepeat = 1 << 1, kPingPong = 1 << 2, kClampForever = 1 << 3 }; #define kCurveTimeEpsilon 0.00001F /* AnimationCurves in Maya are represented as time/value keys with 2D tangents which are always of normalized length. From the tangents a slope is calculated (tangent.y / tangent.x) -> (thus length of the tangent doesn't matter) When the slope is multiplied by the time range of the curve (rhs.time - lhs.time) it can be evaluated using a standard hermite interpolator. In the Unity AnimationCurve the slopes are directly stored in the keyframe instead of the 2D tangent vectors. */ ///@TODO: Curve templates suck. /// Lets make some implementation where they share the same data structure and only Evaluate is specialized. template struct KeyframeTpl { // DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (Keyframe) inline static const char* GetTypeString () { return "Keyframe"; } inline static bool IsAnimationChannel () { return false; } inline static bool MightContainPPtr () { return false; } // Disable transfer optimization in Editor because tangentMode optimized serialization when reading AssetBundles will corrupt data inline static bool AllowTransferOptimization () { #if UNITY_EDITOR return false; #else return true; #endif } template void Transfer (TransferFunction& transfer); float time; T value; T inSlope; T outSlope; //#if UNITY_EDITOR int tangentMode = 0; //#endif #if UNITY_EDITOR KeyframeTpl () { tangentMode = 0; } #else KeyframeTpl () {} #endif KeyframeTpl (float t, const T& v); friend bool operator < (const KeyframeTpl& lhs, const KeyframeTpl& rhs) { return lhs.time < rhs.time; } }; enum AnimationCurveType { kFloatCurve = 0, kVector3Curve = 1, kQuaternionCurve = 2 }; template class AnimationCurveTpl { public: //TODO: Serialize // DECLARE_SERIALIZE_NO_PPTR (AnimationCurve) template void Transfer (TransferFunction& transfer); /// Stores the curve as a pure cubic function with 4 coefficients struct Cache { int index; float time; float timeEnd; T coeff[4]; Cache () { time = std::numeric_limits::infinity (); index=0; timeEnd = 0.0f; memset(&coeff, 0, sizeof(coeff)); } void Invalidate () { time = std::numeric_limits::infinity (); index=0; } }; typedef KeyframeTpl Keyframe; typedef dynamic_array KeyframeContainer; typedef typename KeyframeContainer::iterator iterator; typedef typename KeyframeContainer::const_iterator const_iterator; public: AnimationCurveTpl () { m_PreInfinity = m_PostInfinity = kInternalClamp; } /// Evaluates the AnimationCurve caching the segment. T Evaluate (float curveT) const; T EvaluateClamp (float curveT) const; bool IsValid () const { return m_Curve.size () >= 2; } int AddKey (const Keyframe& key); /// Performs no error checking. And doesn't invalidate the cache! void AddKeyBackFast (const Keyframe& key) { m_Curve.push_back (key); } const Keyframe& GetKey (int index) const { AssertMsg(index >= 0 && index < m_Curve.size(), "Index (%d) is out of range [0, %i)", index, (int)m_Curve.size()); return m_Curve[index]; } /// When changing the keyframe using GetKey you are not allowed to change the time! /// After modifying a key you have to call InvalidateCache Keyframe& GetKey (int index) { AssertMsg(index >= 0 && index < m_Curve.size(), "Index (%d) is out of range [0, %i)", index, (int)m_Curve.size()); return const_cast (m_Curve[index]); } iterator begin () { return m_Curve.begin (); } iterator end () { return m_Curve.end (); } const_iterator begin () const { return m_Curve.begin (); } const_iterator end () const { return m_Curve.end (); } void InvalidateCache (); int GetKeyCount () const { return m_Curve.size (); } void RemoveKeys (iterator begin, iterator end); /// Returns the first and last keyframe time std::pair GetRange () const; enum { kInternalPingPong = 0, kInternalRepeat = 1, kInternalClamp = 2 }; // How does the curve before the first keyframe void SetPreInfinity (int pre); int GetPreInfinity () const; // How does the curve behave after the last keyframe void SetPostInfinity (int post); int GetPostInfinity () const; // How does the curve before the first keyframe void SetPreInfinityInternal (int pre) { m_PreInfinity = pre; InvalidateCache (); } int GetPreInfinityInternal () const { return m_PreInfinity; } // How does the curve behave after the last keyframe void SetPostInfinityInternal (int post) { m_PostInfinity = post; InvalidateCache (); } int GetPostInfinityInternal () const { return m_PostInfinity; } void Assign (const Keyframe* begin, const Keyframe* end) { m_Curve.assign (begin, end); InvalidateCache(); } void Swap (KeyframeContainer& newArray) { m_Curve.swap(newArray); InvalidateCache(); } void Sort () { std::sort(m_Curve.begin(), m_Curve.end()); InvalidateCache(); } void ResizeUninitialized (int size) { m_Curve.resize_uninitialized(size); } ///@TODO: Cleanup old code to completely get rid of this int FindIndex (const Cache& cache, float curveT) const; ///@TODO: Cleanup old code to completely get rid of this /// Returns the closest keyframe index that is less than time. /// Returns -1 if time is outside the range of the curve int FindIndex (float time) const; void CalculateCacheData (Cache& cache, int lhs, int rhs, float timeOffset) const; private: void FindIndexForSampling (const Cache& cache, float curveT, int& lhs, int& rhs) const; /// Evaluates the AnimationCurve directly. void EvaluateWithoutCache (float curveT, T& output)const; float WrapTime (float curveT) const; mutable Cache m_Cache; mutable Cache m_ClampCache; KeyframeContainer m_Curve; int m_PreInfinity; int m_PostInfinity; }; typedef AnimationCurveTpl AnimationCurveBase; typedef AnimationCurveTpl AnimationCurve; typedef AnimationCurveTpl AnimationCurveQuat; typedef AnimationCurveTpl AnimationCurveVec3; template template inline void KeyframeTpl::Transfer (TransferFunction& transfer) { TRANSFER (time); TRANSFER (value); TRANSFER (inSlope); TRANSFER (outSlope); #if UNITY_EDITOR if (!transfer.IsSerializingForGameRelease()) TRANSFER (tangentMode); #endif } template template inline void AnimationCurveTpl::Transfer (TransferFunction& transfer) { transfer.SetVersion(2); //TODO: Transfer dynamic_array transfer.Transfer (m_Curve, "m_Curve");//, kHideInEditorMask); transfer.Transfer (m_PreInfinity, "m_PreInfinity");//, kHideInEditorMask); transfer.Transfer (m_PostInfinity, "m_PostInfinity");//, kHideInEditorMask); if (transfer.IsReading ()) InvalidateCache (); } inline int TimeToFrame (float time, float sampleRate) { return RoundfToInt(time * sampleRate); } inline float FrameToTime (int frame, float sampleRate) { return (float)frame / sampleRate; } inline float FloatFrameToTime (float frame, float sampleRate) { return frame / sampleRate; } void HandleSteppedCurve (const KeyframeTpl& lhs, const KeyframeTpl& rhs, float& value); void HandleSteppedTangent (const KeyframeTpl& lhs, const KeyframeTpl& rhs, float& value); void HandleSteppedCurve (const KeyframeTpl& lhs, const KeyframeTpl& rhs, Vector3f& value); void HandleSteppedTangent (const KeyframeTpl& lhs, const KeyframeTpl& rhs, Vector3f& tangent); void HandleSteppedCurve (const KeyframeTpl& lhs, const KeyframeTpl& rhs, Quaternionf& tangent); void HandleSteppedTangent (const KeyframeTpl& lhs, const KeyframeTpl& rhs, Quaternionf& tangent); inline float PingPong (float t, float length) { t = Repeat (t, length * 2.0F); t = length - Abs (t - length); return t; } inline float Repeat (float t, float begin, float end) { return Repeat (t - begin, end - begin) + begin; } inline double RepeatD (double t, double begin, double end) { return RepeatD (t - begin, end - begin) + begin; } inline float PingPong (float t, float begin, float end) { return PingPong (t - begin, end - begin) + begin; } #if (defined(__GNUC__) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 3)) || defined(__clang__) // in GCC 4.3 and above the explicit template specialization cannot have a storage class #define SPEC_STORAGE_CLASS inline #else #define SPEC_STORAGE_CLASS static #endif #define kMaxTan 5729577.9485111479F template static T MaxTan () { return kMaxTan; } template<> SPEC_STORAGE_CLASS Quaternionf MaxTan () { return Quaternionf(kMaxTan, kMaxTan, kMaxTan, kMaxTan); } template<> SPEC_STORAGE_CLASS Vector3f MaxTan () { return Vector3f(kMaxTan, kMaxTan, kMaxTan); } #undef kMaxTan template static T Zero () { return T (); } template<> SPEC_STORAGE_CLASS Quaternionf Zero () { return Quaternionf(0.0F, 0.0F, 0.0F, 0.0F); } template<> SPEC_STORAGE_CLASS Vector3f Zero () { return Vector3f(0.0F, 0.0F, 0.0F); } #undef SPEC_STORAGE_CLASS void ScaleCurveValue (AnimationCurve& curve, float scale); void OffsetCurveValue (AnimationCurve& curve, float offset); void ScaleCurveTime (AnimationCurve& curve, float scale); void OffsetCurveTime (AnimationCurve& curve, float offset); template inline T HermiteInterpolate (float t, T p0, T m0, T m1, T p1) { float t2 = t * t; float t3 = t2 * t; float a = 2.0F * t3 - 3.0F * t2 + 1.0F; float b = t3 - 2.0F * t2 + t; float c = t3 - t2; float d = -2.0F * t3 + 3.0F * t2; return a * p0 + b * m0 + c * m1 + d * p1; } struct KeyframeCompare { template bool operator ()(KeyframeTpl const& k, float t) { return k.time < t; } // These are necessary for debug STL (validation of predicates) template bool operator ()(KeyframeTpl const& k1, KeyframeTpl const& k2) { return k1.time < k2.time; } template bool operator ()(float t, KeyframeTpl const& k) { return !operator() (k, t); } }; NS_RRP_END #endif /* AnimationCurve_h */