Implemented Animation Clip asset and animation controller #410

Merged
XiaoQiDigipen merged 66 commits from SP3-22-AnimationController into main 2023-03-09 16:19:40 +08:00
9 changed files with 94 additions and 77 deletions
Showing only changes of commit 36af939c67 - Show all commits

View File

@ -1,65 +1,27 @@
/************************************************************************************//*!
\file SHRawAnimation.cpp
\file SHAnimationClip.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Nov 20, 2022
\brief Contains the function definitions of the SHRawAnimation class.
\date Feb 27, 2023
\brief Contains the function definitions of the SHAnimationClip class.
Copyright (C) 2022 DigiPen Institute of Technology.
Copyright (C) 2023 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// Pre-compiled Header
#include "SHpch.h"
// Primary Header
#include "SHRawAnimation.h"
#include "SHAnimationClip.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors */
/*-----------------------------------------------------------------------------------*/
SHRawAnimation::SHRawAnimation(const SHAnimAsset& asset)
: ticksPerSecond { static_cast<int>(asset.ticksPerSecond) }
, totalTime { static_cast<float>(asset.duration) / static_cast<int>(asset.ticksPerSecond) }
{
// Populate keyframes
for (const auto& channel : asset.nodeChannels)
{
// Create a channel
Channel newChannel;
newChannel.Name = std::string(channel.name);
newChannel.PositionKeyFrames.reserve(channel.positionKeys.size());
newChannel.RotationKeyFrames.reserve(channel.rotationKeys.size());
newChannel.ScaleKeyFrames.reserve(channel.scaleKeys.size());
// Populate Keyframes
for (const auto& posKey : channel.positionKeys)
{
newChannel.PositionKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ static_cast<int>(posKey.time), posKey.value});
}
for (const auto& rotKey : channel.rotationKeys)
{
newChannel.RotationKeyFrames.emplace_back(SHAnimationKeyFrame<SHQuaternion>{ static_cast<int>(rotKey.time), rotKey.value});
}
for (const auto& scaleKey : channel.scaleKeys)
{
newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ static_cast<int>(scaleKey.time), scaleKey.value});
}
newChannel.MaxFrames = std::max({ newChannel.PositionKeyFrames.size(), newChannel.RotationKeyFrames.size(), newChannel.ScaleKeyFrames.size() });
// Insert the channel
channels.emplace_back(std::move(newChannel));
}
}
/*-----------------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
SHAnimationClip::SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame)
: rawAnim { rawAnimHandle }
, startFrameIndex { firstFrame }
, endFrameIndex { lastFrame }
{}
}

View File

@ -2,10 +2,10 @@
\file SHAnimationClip.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 12, 2022
\date Feb 27, 2023
\brief Contains the definition of the SHAnimationClip struct and related types.
Copyright (C) 2022 DigiPen Institute of Technology.
Copyright (C) 2023 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
@ -33,19 +33,23 @@ namespace SHADE
class SH_API SHAnimationClip
{
public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------*/
/* Constructors */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Constructs an animation clip that contains the following parameters.
/// </summary>
/// <param name="rawAnimHandle">Handle to the raw animation data.</param>
/// <param name="firstFrame">First frame to be played.</param>
/// <param name="lastFrame">Last frame to be played.</param>
SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame);
/*---------------------------------------------------------------------------------*/
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
inline Handle<SHRawAnimation> GetRawAnimation() const noexcept { return rawAnim; }
inline float GetTotalTime() const noexcept { }
inline int GetStartFrameIndex() const noexcept { return startFrameIndex; }
inline int GetEndFrameIndex() const noexcept { return endFrameIndex; }
private:
/*---------------------------------------------------------------------------------*/
@ -54,11 +58,5 @@ namespace SHADE
Handle<SHRawAnimation> rawAnim;
int startFrameIndex; // First Frame
int endFrameIndex; // Last Frame (inclusive)
float totalTime; // Time to take from first to last frame
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
};
}

View File

@ -92,15 +92,27 @@ namespace SHADE
if (currClip == newClip)
return;
// Get animation data
auto animData = currClip->GetRawAnimation();
if (!animData)
return;
// Set parameters
currClip = newClip;
secsPerTick = 1.0f / currClip->GetTicksPerSecond();
secsPerTick = 1.0f / animData->GetTicksPerSecond();
// Build channel map
channelMap.clear();
// Calculate total time for the clip
if (currClip)
{
for (const auto& channel : currClip->GetChannels())
const int ONE_PAST_LAST_FRAME = currClip->GetEndFrameIndex() + 1;
currClipTotalTime = (ONE_PAST_LAST_FRAME - currClip->GetStartFrameIndex()) / animData->GetTicksPerSecond();
}
// Build channel map and clip-specific data
channelMap.clear();
if (animData)
{
for (const auto& channel : animData->GetChannels())
{
channelMap.emplace(channel.Name, &channel);
}
@ -131,7 +143,7 @@ namespace SHADE
// Update time on the playback
currPlaybackTime += dt;
if (currPlaybackTime > animData->GetTotalTime())
if (currPlaybackTime > currClipTotalTime)
{
if (playOnce)
{
@ -140,7 +152,7 @@ namespace SHADE
}
else
{
currPlaybackTime = currPlaybackTime - animData->GetTotalTime();
currPlaybackTime = currPlaybackTime - currClipTotalTime;
}
}
@ -154,7 +166,7 @@ namespace SHADE
void SHAnimatorComponent::updatePoseWithClip(float poseTime)
{
// Get closest frame index
const int CLOSEST_FRAME_IDX = static_cast<int>(std::floorf(poseTime * currClip->GetTicksPerSecond()));
const int CLOSEST_FRAME_IDX = currClip->GetStartFrameIndex() + static_cast<int>(std::floorf(poseTime * currClip->GetRawAnimation()->GetTicksPerSecond()));
updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity);
}

View File

@ -136,6 +136,7 @@ namespace SHADE
float currPlaybackTime = 0.0f;
bool isPlaying = true;
bool playOnce = false;
float currClipTotalTime = 0.0f;
// Useful Cached Data
float secsPerTick = 0.0f;
// Buffer

View File

@ -0,0 +1,28 @@
/************************************************************************************//*!
\file SHAnimClipAsset.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Feb 27, 2023
\brief Contains the definition of the SHAnimationClip struct and related types.
Copyright (C) 2023 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
#pragma once
#include <string>
#include "SH_API.h"
#include "SHAssetData.h"
namespace SHADE
{
struct SH_API SHAnimClipAsset : SHAssetData
{
std::string name;
AssetID animRawDataAssetId;
int firstIndex;
int lastIndex;
};
}

View File

@ -27,6 +27,8 @@
#include "Physics/Collision/Shapes/SHSphere.h"
#include "../SHEditorWindowManager.h"
#include "../AssetBrowser/SHAssetBrowser.h"
#include "Animation/SHAnimationClip.h"
namespace SHADE
{
template<typename T>
@ -644,12 +646,12 @@ namespace SHADE
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID);
}
}
Handle<SHRawAnimation> const& clip = component->GetCurrentClip();
Handle<SHRawAnimation> const clip = (component->GetCurrentClip() ? component->GetCurrentClip()->GetRawAnimation() : Handle<SHRawAnimation>{});
const auto CLIP_NAME = clip ? SHResourceManager::GetAssetName<SHRawAnimation>(clip).value_or("") : "";
SHEditorWidgets::DragDropReadOnlyField<AssetID>("Clip", CLIP_NAME,
[component]()
{
Handle<SHRawAnimation> const& clip = component->GetCurrentClip();
Handle<SHRawAnimation> const clip = (component->GetCurrentClip() ? component->GetCurrentClip()->GetRawAnimation() : Handle<SHRawAnimation>{});
return SHResourceManager::GetAssetID<SHRawAnimation>(clip).value_or(0);
},
[component](AssetID const& id)
@ -659,11 +661,11 @@ namespace SHADE
SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!")
return;
}
component->SetClip(SHResourceManager::LoadOrGet<SHRawAnimation>(id));
component->SetClip(SHResourceManager::LoadOrGet<SHAnimationClip>(id));
}, SHDragDrop::DRAG_RESOURCE);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
if (Handle<SHRawAnimation> const& clip = component->GetCurrentClip())
if (Handle<SHRawAnimation> const clip = component->GetCurrentClip()->GetRawAnimation())
{
AssetID assetID = SHResourceManager::GetAssetID<SHRawAnimation>(clip).value_or(0);
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID);

View File

@ -21,6 +21,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Assets/Asset Types/Models/SHModelAsset.h"
#include "Assets/Asset Types/SHTextureAsset.h"
#include "Assets/Asset Types/SHShaderAsset.h"
#include "Assets/Asset Types/SHAnimClipAsset.h"
#include "Graphics/Shaders/SHVkShaderModule.h"
#include "Graphics/MiddleEnd/Textures/SHTextureLibrary.h"
#include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h"
@ -38,6 +39,7 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/
class SHMaterial;
struct SHRigNode;
class SHAnimationClip;
/*-----------------------------------------------------------------------------------*/
/* Type Definitions */
@ -53,8 +55,9 @@ namespace SHADE
template<> struct SHResourceLoader<SHMaterialSpec> { using AssetType = SHMaterialAsset; };
template<> struct SHResourceLoader<SHMaterial> { using AssetType = SHMaterialSpec; };
template<> struct SHResourceLoader<SHFont> { using AssetType = SHFontAsset; };
template<> struct SHResourceLoader<SHRawAnimation> { using AssetType = SHModelAsset; };
template<> struct SHResourceLoader<SHRig> { using AssetType = SHModelAsset; };
template<> struct SHResourceLoader<SHRawAnimation> { using AssetType = SHModelAsset; };
template<> struct SHResourceLoader<SHRig> { using AssetType = SHModelAsset; };
template<> struct SHResourceLoader<SHAnimationClip> { using AssetType = SHAnimClipAsset; };
/// <summary>
/// Static class responsible for loading and caching runtime resources from their

View File

@ -25,6 +25,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h"
#include "Serialization/SHYAMLConverters.h"
#include "Animation/SHAnimationClip.h"
namespace SHADE
{
@ -360,5 +361,15 @@ namespace SHADE
loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType>(*assetData.anims[0]);
}
else if constexpr (std::is_same_v<ResourceType, SHAnimClipAsset>)
{
loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType>
(
LoadOrGet<SHRawAnimation>(assetData->animRawDataAssetId),
assetData->firstIndex,
assetData->lastIndex
);
}
}
}

View File

@ -400,7 +400,7 @@ namespace YAML
{
YAML::Node node;
node[RIG_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHRig>(rhs.GetRig()).value_or(0);
node[CLIP_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHRawAnimation>(rhs.GetCurrentClip()).value_or(0);
node[CLIP_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHAnimationClip>(rhs.GetCurrentClip()).value_or(0);
return node;
}
static bool decode(YAML::Node const& node, SHAnimatorComponent& rhs)
@ -411,7 +411,7 @@ namespace YAML
}
if (node[CLIP_YAML_TAG.data()].IsDefined())
{
rhs.SetClip(SHResourceManager::LoadOrGet<SHRawAnimation>(node[CLIP_YAML_TAG.data()].as<AssetID>()));
rhs.SetClip(SHResourceManager::LoadOrGet<SHAnimationClip>(node[CLIP_YAML_TAG.data()].as<AssetID>()));
}
return true;
}