Added support for changing playback speed of an animation clip

This commit is contained in:
Kah Wei 2023-03-14 14:07:46 +08:00
parent b443f32388
commit f844079eea
8 changed files with 55 additions and 21 deletions

View File

@ -20,12 +20,13 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Constructors */ /* Constructors */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHAnimationClip::SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame) SHAnimationClip::SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame, float playbackMultiplier)
: rawAnim { rawAnimHandle } : rawAnim { rawAnimHandle }
, startFrameIndex { firstFrame } , startFrameIndex { firstFrame }
, endFrameIndex { lastFrame } , endFrameIndex { lastFrame }
, duration { 0.0f } , duration { 0.0f }
, startTimeStamp { 0.0f } , startTimeStamp { 0.0f }
, playbackSpeedMultiplier { playbackMultiplier }
{ {
if (!rawAnim) if (!rawAnim)
return; return;

View File

@ -41,25 +41,39 @@ namespace SHADE
/// <param name="rawAnimHandle">Handle to the raw animation data.</param> /// <param name="rawAnimHandle">Handle to the raw animation data.</param>
/// <param name="firstFrame">First frame to be played.</param> /// <param name="firstFrame">First frame to be played.</param>
/// <param name="lastFrame">Last frame to be played.</param> /// <param name="lastFrame">Last frame to be played.</param>
SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame); /// <param name="playbackMultiplier">Multiplier for the playback speed.</param>
SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame, float playbackMultiplier = 1.0f);
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
inline Handle<SHRawAnimation> GetRawAnimation() const noexcept { return rawAnim; } inline Handle<SHRawAnimation> GetRawAnimation() const noexcept { return rawAnim; }
inline int GetStartFrameIndex() const noexcept { return startFrameIndex; } inline int GetStartFrameIndex() const noexcept { return startFrameIndex; }
inline int GetEndFrameIndex() const noexcept { return endFrameIndex; } inline int GetEndFrameIndex() const noexcept { return endFrameIndex; }\
inline float GetTotalDuration() const noexcept { return duration; }
inline float GetStartTimeStamp() const noexcept { return startTimeStamp; } inline float GetStartTimeStamp() const noexcept { return startTimeStamp; }
inline float GetPlaybackSpeedMultiplier() const noexcept { return playbackSpeedMultiplier; }
/// <summary>
/// Retrieves the duration of the animation as if the playback speed multiplier is
/// in it's default value of 1.0f.
/// </summary>
/// <returns>Duration of the animation in seconds.</returns>
inline float GetTotalDuration() const noexcept { return duration; }
/// <summary>
/// Retrieves the duration of the animation with the playback speed multiplier
/// taken into account.
/// </summary>
/// <returns>True duration of the animation in seconds.</returns>
inline float GetTrueDuration() const noexcept { return duration / playbackSpeedMultiplier; }
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
Handle<SHRawAnimation> rawAnim; Handle<SHRawAnimation> rawAnim;
int startFrameIndex; // First Frame int startFrameIndex; // First Frame
int endFrameIndex; // Last Frame (inclusive) int endFrameIndex; // Last Frame (inclusive)
float duration; // Total playback time float duration; // Total playback time
float startTimeStamp; // Starting time stamp of the raw anim float startTimeStamp; // Starting time stamp of the raw anim
float playbackSpeedMultiplier; // Multiplier applied to the playback of an animation clip
}; };
} }

View File

@ -62,18 +62,21 @@ namespace SHADE
if (!instData.CurrentNode) if (!instData.CurrentNode)
return; return;
// Get the clip
Handle<SHAnimationClip> clip = instData.CurrentNode->Clip;
// Update the current playback // Update the current playback
instData.ClipPlaybackTime += dt; instData.ClipPlaybackTime += dt * clip->GetPlaybackSpeedMultiplier();
// Check if we finished playing // Check if we finished playing
const float CLIP_CURR_PLAYED_TIME = instData.ClipPlaybackTime - instData.CurrentNode->Clip->GetStartTimeStamp(); const float CLIP_CURR_PLAYED_TIME = instData.ClipPlaybackTime - clip->GetStartTimeStamp();
if (CLIP_CURR_PLAYED_TIME > instData.CurrentNode->Clip->GetTotalDuration()) if (CLIP_CURR_PLAYED_TIME > clip->GetTotalDuration())
{ {
// Clamp // Clamp
instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetStartTimeStamp() + instData.CurrentNode->Clip->GetTotalDuration(); instData.ClipPlaybackTime = clip->GetStartTimeStamp() + clip->GetTotalDuration();
// Go to next state // Go to next state
Handle<SHAnimationClip> originalClip = instData.CurrentNode->Clip; Handle<SHAnimationClip> originalClip = clip;
bool stateChanged = false; bool stateChanged = false;
for (const auto& transition : instData.CurrentNode->Transitions) for (const auto& transition : instData.CurrentNode->Transitions)
{ {

View File

@ -254,7 +254,7 @@ namespace SHADE
} }
void SHAnimatorComponent::updateManualClipState(float dt) void SHAnimatorComponent::updateManualClipState(float dt)
{ {
currPlaybackTime += dt; currPlaybackTime += dt * currClip->GetPlaybackSpeedMultiplier();
const float CLIP_CURR_PLAYED_TIME = currPlaybackTime - currClip->GetStartTimeStamp(); const float CLIP_CURR_PLAYED_TIME = currPlaybackTime - currClip->GetStartTimeStamp();
if (CLIP_CURR_PLAYED_TIME > currClip->GetTotalDuration()) if (CLIP_CURR_PLAYED_TIME > currClip->GetTotalDuration())
{ {

View File

@ -26,6 +26,7 @@ namespace SHADE
uint32_t firstIndex; uint32_t firstIndex;
uint32_t lastIndex; uint32_t lastIndex;
AssetID animRawDataAssetId; // Not serialised, only populated during runtime from parent asset AssetID animRawDataAssetId; // Not serialised, only populated during runtime from parent asset
float playbackMultiplier = 1.0f;
}; };
struct SH_API SHAnimClipContainerAsset final : SHAssetData struct SH_API SHAnimClipContainerAsset final : SHAssetData

View File

@ -54,6 +54,7 @@ namespace SHADE
newAssetName.clear(); newAssetName.clear();
firstIndex = 0; firstIndex = 0;
lastIndex = rawAnimation->GetTotalFrames(); lastIndex = rawAnimation->GetTotalFrames();
playbackMultiplier = 1.0f;
} }
// Assign callback // Assign callback
@ -72,6 +73,9 @@ namespace SHADE
SHEditorUI::PushID(1); SHEditorUI::PushID(1);
SHEditorUI::InputUnsignedInt("Last Frame Index", lastIndex); SHEditorUI::InputUnsignedInt("Last Frame Index", lastIndex);
SHEditorUI::PopID(); SHEditorUI::PopID();
SHEditorUI::PushID(2);
SHEditorUI::InputFloat("Playback Multiplier", playbackMultiplier);
SHEditorUI::PopID();
// Invalid values // Invalid values
const bool INVALID_CONFIG = newAssetName.empty() || firstIndex > lastIndex; const bool INVALID_CONFIG = newAssetName.empty() || firstIndex > lastIndex;
@ -88,6 +92,7 @@ namespace SHADE
animClip->firstIndex = firstIndex; animClip->firstIndex = firstIndex;
animClip->lastIndex = lastIndex; animClip->lastIndex = lastIndex;
animClip->animRawDataAssetId = SHResourceManager::GetAssetID<SHRawAnimation>(rawAnimation).value_or(0); animClip->animRawDataAssetId = SHResourceManager::GetAssetID<SHRawAnimation>(rawAnimation).value_or(0);
animClip->playbackMultiplier = playbackMultiplier;
SHAssetManager::SaveAsset(containerAsset->id); SHAssetManager::SaveAsset(containerAsset->id);
// Close // Close
@ -168,6 +173,7 @@ namespace SHADE
int firstIndex = animClip->GetStartFrameIndex(); int firstIndex = animClip->GetStartFrameIndex();
int endIndex = animClip->GetEndFrameIndex(); int endIndex = animClip->GetEndFrameIndex();
float playbackMp = animClip->GetPlaybackSpeedMultiplier();
ImGui::Separator(); ImGui::Separator();
ImGui::Text(animClipName.has_value() ? animClipName.value().c_str() : ""); ImGui::Text(animClipName.has_value() ? animClipName.value().c_str() : "");
@ -183,12 +189,18 @@ namespace SHADE
[&]() { return endIndex; }, [&]() { return endIndex; },
[&](int i) { endIndex = i; } [&](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 there's a change we need to commit changes
if (changed && firstIndex < endIndex) if (changed && firstIndex < endIndex)
{ {
// Update runtime asset // Update runtime asset
*animClip = SHAnimationClip(currRawAnim, firstIndex, endIndex); *animClip = SHAnimationClip(currRawAnim, firstIndex, endIndex, playbackMp);
// Update serialized asset // Update serialized asset
auto assetId = SHResourceManager::GetAssetID(animClip); auto assetId = SHResourceManager::GetAssetID(animClip);
@ -197,6 +209,7 @@ namespace SHADE
auto const animAsset = SHAssetManager::GetData<SHAnimClipAsset>(assetId.value()); auto const animAsset = SHAssetManager::GetData<SHAnimClipAsset>(assetId.value());
animAsset->firstIndex = firstIndex; animAsset->firstIndex = firstIndex;
animAsset->lastIndex = endIndex; animAsset->lastIndex = endIndex;
animAsset->playbackMultiplier = playbackMp;
SHAssetManager::SaveAsset(containerAsset->id); SHAssetManager::SaveAsset(containerAsset->id);
} }
} }

View File

@ -62,6 +62,7 @@ namespace SHADE
std::string newAssetName; std::string newAssetName;
uint32_t firstIndex = 0; uint32_t firstIndex = 0;
uint32_t lastIndex = 0; uint32_t lastIndex = 0;
float playbackMultiplier = 1.0f;
Handle<SHRawAnimation> rawAnimation; Handle<SHRawAnimation> rawAnimation;
SHAsset* containerAsset{nullptr}; SHAsset* containerAsset{nullptr};
SHAnimClipContainerAsset* container{nullptr}; SHAnimClipContainerAsset* container{nullptr};

View File

@ -372,7 +372,8 @@ namespace SHADE
( (
LoadOrGet<SHRawAnimation>(assetData.animRawDataAssetId), LoadOrGet<SHRawAnimation>(assetData.animRawDataAssetId),
assetData.firstIndex, assetData.firstIndex,
assetData.lastIndex assetData.lastIndex,
assetData.playbackMultiplier
); );
} }
else if constexpr (std::is_same_v<ResourceType, SHAnimationController>) else if constexpr (std::is_same_v<ResourceType, SHAnimationController>)