// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Core/Types/String.h" #include "Engine/Animations/Curve.h" #include "Engine/Core/Math/Transform.h" /// /// Single node animation data container. /// struct NodeAnimationData { public: /// /// The target node name. /// String NodeName; /// /// The position channel animation. /// LinearCurve Position; /// /// The rotation channel animation. /// LinearCurve Rotation; /// /// The scale channel animation. /// LinearCurve Scale; public: /// /// Initializes a new instance of the class. /// NodeAnimationData() : Position(Float3::Zero) , Rotation(Quaternion::Identity) , Scale(Float3::One) { } public: /// /// Evaluates the animation transformation at the specified time (only for the curves with non-empty data). /// /// The time to evaluate the curves at. /// The interpolated value from the curve at provided time. /// If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped. void Evaluate(float time, Transform* result, bool loop = true) const { if (Position.GetKeyframes().HasItems()) { Float3 position; Position.Evaluate(position, time, loop); result->Translation = position; } if (Rotation.GetKeyframes().HasItems()) Rotation.Evaluate(result->Orientation, time, loop); if (Scale.GetKeyframes().HasItems()) Scale.Evaluate(result->Scale, time, loop); } /// /// Evaluates the animation transformation at the specified time. /// /// The time to evaluate the curves at. /// The interpolated value from the curve at provided time. /// If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped. void EvaluateAll(float time, Transform* result, bool loop = true) const { Float3 position; Position.Evaluate(position, time, loop); result->Translation = position; Rotation.Evaluate(result->Orientation, time, loop); Scale.Evaluate(result->Scale, time, loop); } /// /// Gets the total amount of keyframes in the animation curves. /// int32 GetKeyframesCount() const { return Position.GetKeyframes().Count() + Rotation.GetKeyframes().Count() + Scale.GetKeyframes().Count(); } uint64 GetMemoryUsage() const { return NodeName.Length() * sizeof(Char) + Position.GetMemoryUsage() + Rotation.GetMemoryUsage() + Scale.GetMemoryUsage(); } }; /// /// Skeleton nodes animation data container. Includes metadata about animation sampling, duration and node animations curves. /// struct AnimationData { public: /// /// The duration of the animation (in frames). /// double Duration = 0.0; /// /// The amount of the animation frames per second. /// double FramesPerSecond = 0.0; /// /// Enables root motion extraction support from this animation. /// bool EnableRootMotion = false; /// /// The custom node name to be used as a root motion source. If not specified the actual root node will be used. /// String RootNodeName; /// /// The per skeleton node animation channels. /// Array Channels; public: /// /// Gets the length of the animation (in seconds). /// FORCE_INLINE float GetLength() const { #if BUILD_DEBUG ASSERT(FramesPerSecond != 0); #endif return static_cast(Duration / FramesPerSecond); } uint64 GetMemoryUsage() const { uint64 result = RootNodeName.Length() * sizeof(Char) + Channels.Capacity() * sizeof(NodeAnimationData); for (const auto& e : Channels) result += e.GetMemoryUsage(); return result; } /// /// Gets the total amount of keyframes in the all animation channels. /// int32 GetKeyframesCount() const { int32 result = 0; for (int32 i = 0; i < Channels.Count(); i++) { result += Channels[i].GetKeyframesCount(); } return result; } /// /// Swaps the contents of object with the other object without copy operation. Performs fast internal data exchange. /// /// The other object. void Swap(AnimationData& other) { ::Swap(Duration, other.Duration); ::Swap(FramesPerSecond, other.FramesPerSecond); ::Swap(EnableRootMotion, other.EnableRootMotion); ::Swap(RootNodeName, other.RootNodeName); Channels.Swap(other.Channels); } /// /// Releases data. /// void Dispose() { Duration = 0.0; FramesPerSecond = 0.0; RootNodeName.Clear(); EnableRootMotion = false; Channels.Resize(0); } };