From f844079eea4ca611e19793bdd6c214a749daa77f Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 14 Mar 2023 14:07:46 +0800 Subject: [PATCH] Added support for changing playback speed of an animation clip --- .../src/Animation/SHAnimationClip.cpp | 13 +++++---- SHADE_Engine/src/Animation/SHAnimationClip.h | 28 ++++++++++++++----- .../src/Animation/SHAnimationController.cpp | 13 +++++---- .../src/Animation/SHAnimatorComponent.cpp | 2 +- .../Asset Types/SHAnimClipContainerAsset.h | 1 + .../SHRawAnimInspector.cpp | 15 +++++++++- .../SHRawAnimInspector.h | 1 + .../src/Resource/SHResourceManager.hpp | 3 +- 8 files changed, 55 insertions(+), 21 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index 2066506f..b45701d8 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -20,12 +20,13 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Constructors */ /*-----------------------------------------------------------------------------------*/ - SHAnimationClip::SHAnimationClip(Handle rawAnimHandle, int firstFrame, int lastFrame) - : rawAnim { rawAnimHandle } - , startFrameIndex { firstFrame } - , endFrameIndex { lastFrame } - , duration { 0.0f } - , startTimeStamp { 0.0f } + SHAnimationClip::SHAnimationClip(Handle rawAnimHandle, int firstFrame, int lastFrame, float playbackMultiplier) + : rawAnim { rawAnimHandle } + , startFrameIndex { firstFrame } + , endFrameIndex { lastFrame } + , duration { 0.0f } + , startTimeStamp { 0.0f } + , playbackSpeedMultiplier { playbackMultiplier } { if (!rawAnim) return; diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index 6b97c955..2c6b6561 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -41,25 +41,39 @@ namespace SHADE /// Handle to the raw animation data. /// First frame to be played. /// Last frame to be played. - SHAnimationClip(Handle rawAnimHandle, int firstFrame, int lastFrame); + /// Multiplier for the playback speed. + SHAnimationClip(Handle rawAnimHandle, int firstFrame, int lastFrame, float playbackMultiplier = 1.0f); /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ inline Handle GetRawAnimation() const noexcept { return rawAnim; } inline int GetStartFrameIndex() const noexcept { return startFrameIndex; } - inline int GetEndFrameIndex() const noexcept { return endFrameIndex; } - inline float GetTotalDuration() const noexcept { return duration; } + inline int GetEndFrameIndex() const noexcept { return endFrameIndex; }\ inline float GetStartTimeStamp() const noexcept { return startTimeStamp; } + inline float GetPlaybackSpeedMultiplier() const noexcept { return playbackSpeedMultiplier; } + /// + /// Retrieves the duration of the animation as if the playback speed multiplier is + /// in it's default value of 1.0f. + /// + /// Duration of the animation in seconds. + inline float GetTotalDuration() const noexcept { return duration; } + /// + /// Retrieves the duration of the animation with the playback speed multiplier + /// taken into account. + /// + /// True duration of the animation in seconds. + inline float GetTrueDuration() const noexcept { return duration / playbackSpeedMultiplier; } private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ Handle rawAnim; - int startFrameIndex; // First Frame - int endFrameIndex; // Last Frame (inclusive) - float duration; // Total playback time - float startTimeStamp; // Starting time stamp of the raw anim + int startFrameIndex; // First Frame + int endFrameIndex; // Last Frame (inclusive) + float duration; // Total playback time + float startTimeStamp; // Starting time stamp of the raw anim + float playbackSpeedMultiplier; // Multiplier applied to the playback of an animation clip }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimationController.cpp b/SHADE_Engine/src/Animation/SHAnimationController.cpp index 3c04614a..e105c0da 100644 --- a/SHADE_Engine/src/Animation/SHAnimationController.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationController.cpp @@ -62,18 +62,21 @@ namespace SHADE if (!instData.CurrentNode) return; + // Get the clip + Handle clip = instData.CurrentNode->Clip; + // Update the current playback - instData.ClipPlaybackTime += dt; + instData.ClipPlaybackTime += dt * clip->GetPlaybackSpeedMultiplier(); // Check if we finished playing - const float CLIP_CURR_PLAYED_TIME = instData.ClipPlaybackTime - instData.CurrentNode->Clip->GetStartTimeStamp(); - if (CLIP_CURR_PLAYED_TIME > instData.CurrentNode->Clip->GetTotalDuration()) + const float CLIP_CURR_PLAYED_TIME = instData.ClipPlaybackTime - clip->GetStartTimeStamp(); + if (CLIP_CURR_PLAYED_TIME > clip->GetTotalDuration()) { // Clamp - instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetStartTimeStamp() + instData.CurrentNode->Clip->GetTotalDuration(); + instData.ClipPlaybackTime = clip->GetStartTimeStamp() + clip->GetTotalDuration(); // Go to next state - Handle originalClip = instData.CurrentNode->Clip; + Handle originalClip = clip; bool stateChanged = false; for (const auto& transition : instData.CurrentNode->Transitions) { diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 294d8098..923fb876 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -254,7 +254,7 @@ namespace SHADE } void SHAnimatorComponent::updateManualClipState(float dt) { - currPlaybackTime += dt; + currPlaybackTime += dt * currClip->GetPlaybackSpeedMultiplier(); const float CLIP_CURR_PLAYED_TIME = currPlaybackTime - currClip->GetStartTimeStamp(); if (CLIP_CURR_PLAYED_TIME > currClip->GetTotalDuration()) { diff --git a/SHADE_Engine/src/Assets/Asset Types/SHAnimClipContainerAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHAnimClipContainerAsset.h index 28d3b697..15fc449d 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHAnimClipContainerAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHAnimClipContainerAsset.h @@ -26,6 +26,7 @@ namespace SHADE uint32_t firstIndex; uint32_t lastIndex; AssetID animRawDataAssetId; // Not serialised, only populated during runtime from parent asset + float playbackMultiplier = 1.0f; }; struct SH_API SHAnimClipContainerAsset final : SHAssetData diff --git a/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.cpp index 332bf6e9..e85efaf4 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.cpp @@ -54,6 +54,7 @@ namespace SHADE newAssetName.clear(); firstIndex = 0; lastIndex = rawAnimation->GetTotalFrames(); + playbackMultiplier = 1.0f; } // Assign callback @@ -72,6 +73,9 @@ namespace SHADE SHEditorUI::PushID(1); SHEditorUI::InputUnsignedInt("Last Frame Index", lastIndex); SHEditorUI::PopID(); + SHEditorUI::PushID(2); + SHEditorUI::InputFloat("Playback Multiplier", playbackMultiplier); + SHEditorUI::PopID(); // Invalid values const bool INVALID_CONFIG = newAssetName.empty() || firstIndex > lastIndex; @@ -88,6 +92,7 @@ namespace SHADE animClip->firstIndex = firstIndex; animClip->lastIndex = lastIndex; animClip->animRawDataAssetId = SHResourceManager::GetAssetID(rawAnimation).value_or(0); + animClip->playbackMultiplier = playbackMultiplier; SHAssetManager::SaveAsset(containerAsset->id); // Close @@ -168,6 +173,7 @@ namespace SHADE int firstIndex = animClip->GetStartFrameIndex(); int endIndex = animClip->GetEndFrameIndex(); + float playbackMp = animClip->GetPlaybackSpeedMultiplier(); ImGui::Separator(); ImGui::Text(animClipName.has_value() ? animClipName.value().c_str() : ""); @@ -183,12 +189,18 @@ namespace SHADE [&]() { return endIndex; }, [&](int i) { endIndex = i; } ); + changed |= SHEditorWidgets::DragFloat + ( + "Playback Multiplier", + [&]() { return playbackMp; }, + [&](float f) { playbackMp = f; } + ); // If there's a change we need to commit changes if (changed && firstIndex < endIndex) { // Update runtime asset - *animClip = SHAnimationClip(currRawAnim, firstIndex, endIndex); + *animClip = SHAnimationClip(currRawAnim, firstIndex, endIndex, playbackMp); // Update serialized asset auto assetId = SHResourceManager::GetAssetID(animClip); @@ -197,6 +209,7 @@ namespace SHADE auto const animAsset = SHAssetManager::GetData(assetId.value()); animAsset->firstIndex = firstIndex; animAsset->lastIndex = endIndex; + animAsset->playbackMultiplier = playbackMp; SHAssetManager::SaveAsset(containerAsset->id); } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h index 6790cded..550bd158 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h +++ b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h @@ -62,6 +62,7 @@ namespace SHADE std::string newAssetName; uint32_t firstIndex = 0; uint32_t lastIndex = 0; + float playbackMultiplier = 1.0f; Handle rawAnimation; SHAsset* containerAsset{nullptr}; SHAnimClipContainerAsset* container{nullptr}; diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 6474b478..d2bc9241 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -372,7 +372,8 @@ namespace SHADE ( LoadOrGet(assetData.animRawDataAssetId), assetData.firstIndex, - assetData.lastIndex + assetData.lastIndex, + assetData.playbackMultiplier ); } else if constexpr (std::is_same_v)