Implemented Animation Clip asset and animation controller #410
|
@ -13,6 +13,7 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "SHpch.h"
|
||||
// Primary Header
|
||||
#include "SHAnimationClip.h"
|
||||
#include "SHRawAnimation.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
@ -23,5 +24,12 @@ namespace SHADE
|
|||
: rawAnim { rawAnimHandle }
|
||||
, startFrameIndex { firstFrame }
|
||||
, endFrameIndex { lastFrame }
|
||||
{}
|
||||
, duration { 0.0f }
|
||||
{
|
||||
if (!rawAnim)
|
||||
return;
|
||||
|
||||
const int ONE_PAST_LAST_FRAME = lastFrame + 1;
|
||||
duration = (ONE_PAST_LAST_FRAME - firstFrame) / rawAnim->GetTicksPerSecond();
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ namespace SHADE
|
|||
inline Handle<SHRawAnimation> 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; }
|
||||
|
||||
private:
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
|
@ -58,5 +59,6 @@ namespace SHADE
|
|||
Handle<SHRawAnimation> rawAnim;
|
||||
int startFrameIndex; // First Frame
|
||||
int endFrameIndex; // Last Frame (inclusive)
|
||||
float duration; // Total playback time
|
||||
};
|
||||
}
|
|
@ -20,21 +20,27 @@ namespace SHADE
|
|||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void SHAnimationController::Update()
|
||||
void SHAnimationController::Update(InstanceData& instData, float dt)
|
||||
{
|
||||
// Is there a valid node
|
||||
if (!currentNode)
|
||||
if (!instData.CurrentNode)
|
||||
return;
|
||||
|
||||
// Update
|
||||
for (const auto& transition : currentNode->Transitions)
|
||||
{
|
||||
// Update the current playback
|
||||
instData.ClipPlaybackTime += dt;
|
||||
|
||||
}
|
||||
}
|
||||
void SHAnimationController::Reset()
|
||||
// Check if we finished playing
|
||||
if (instData.ClipPlaybackTime > instData.CurrentNode->Clip->GetTotalDuration())
|
||||
{
|
||||
currentNode = startNode;
|
||||
// Clamp
|
||||
instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetTotalDuration();
|
||||
|
||||
// Go to next state
|
||||
for (const auto& transition : instData.CurrentNode->Transitions)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
@ -75,4 +81,24 @@ namespace SHADE
|
|||
// Clear node
|
||||
node.Free();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* AnimParam Functions */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
SHAnimationController::AnimParam::AnimParam(Type type)
|
||||
: ParamType { type }
|
||||
{
|
||||
switch (ParamType)
|
||||
{
|
||||
case Type::Bool:
|
||||
Value = false;
|
||||
break;
|
||||
case Type::Float:
|
||||
Value = 0.0f;
|
||||
break;
|
||||
case Type::Int:
|
||||
Value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace SHADE
|
|||
/// <summary>
|
||||
/// Object that controls the animation that is played by an animator through the use
|
||||
/// of an internal state machine.
|
||||
/// This should never be modified once it has been attached to a SHAnimatorComponent!
|
||||
/// </summary>
|
||||
class SH_API SHAnimationController
|
||||
{
|
||||
|
@ -58,12 +59,23 @@ namespace SHADE
|
|||
Float,
|
||||
Int
|
||||
};
|
||||
using ValueType = std::variant<bool, float, int>;
|
||||
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/* Constructor */
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Constructs an AnimParam with the default value set for the Value field based
|
||||
/// on the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of AnimParam.</param>
|
||||
explicit AnimParam(Type type);
|
||||
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/* Data Members */
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
Type ParamType;
|
||||
std::variant<bool, float, int> Value;
|
||||
ValueType Value;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -92,6 +104,7 @@ namespace SHADE
|
|||
/* Data Members */
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
ConditionType Condition;
|
||||
AnimParam::ValueType Threshold;
|
||||
Handle<Node> Target;
|
||||
};
|
||||
|
||||
|
@ -104,17 +117,23 @@ namespace SHADE
|
|||
std::vector<Transition> Transitions;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Describes a node in the animation controller.
|
||||
/// </summary>
|
||||
struct InstanceData
|
||||
{
|
||||
Handle<Node> CurrentNode;
|
||||
std::unordered_map<std::string, AnimParam> Params;
|
||||
float ClipPlaybackTime;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Runs a single update for the animation controller.
|
||||
/// </summary>
|
||||
void Update();
|
||||
/// <summary>
|
||||
/// Resets the animation controller to its starting node.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
void Update(InstanceData& instData, float dt);
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Usage Functions */
|
||||
|
@ -130,20 +149,23 @@ namespace SHADE
|
|||
/// </summary>
|
||||
/// <param name="node">Node to destroy.</param>
|
||||
void DestroyNode(Handle<Node> node);
|
||||
void AddTransition(Handle<Node> source, const Transition& transition);
|
||||
void AddParameter(const std::string& name, AnimParam::Type type);
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Getters */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
Handle<Node> GetCurrentNode() const noexcept { return currentNode; }
|
||||
Handle<Node> GetStartingNode() const noexcept { return startNode; }
|
||||
const std::unordered_map<std::string, AnimParam::Type>& GetParams() const noexcept { return parameters; }
|
||||
const std::vector<Handle<Node>>& GetNodes() const noexcept { return nodes; }
|
||||
|
||||
private:
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Data Members */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
// State machine
|
||||
Handle<Node> currentNode;
|
||||
Handle<Node> startNode;
|
||||
std::vector<Handle<Node>> nodes;
|
||||
std::unordered_map<std::string, AnimParam> parameters;
|
||||
std::unordered_map<std::string, AnimParam::Type> parameters;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -39,9 +39,42 @@ namespace SHADE
|
|||
|
||||
void SHAnimatorComponent::Play(Handle<SHAnimationClip> clip)
|
||||
{
|
||||
// Ignore if nothing is specified
|
||||
if (!clip)
|
||||
{
|
||||
SHLOG_WARNING("[SHAnimatorComponent] Attempted to play an null SHAnimationClip. Use Stop() if stopping animation instead.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove animation controller as we switch to manual play mode
|
||||
animController = {};
|
||||
animInstanceData.CurrentNode = {};
|
||||
animInstanceData.Params.clear();
|
||||
animInstanceData.ClipPlaybackTime = 0.0f;
|
||||
|
||||
// Set accordingly
|
||||
currClip = clip;
|
||||
currPlaybackTime = 0.0f;
|
||||
channelMaps.clear();
|
||||
auto RAW_ANIM = clip->GetRawAnimation();
|
||||
|
||||
// Set up if valid
|
||||
if (currClip && RAW_ANIM)
|
||||
{
|
||||
// Create channel maps
|
||||
channelMaps.emplace(RAW_ANIM, createChannelMap(RAW_ANIM));
|
||||
// Calculate secs for the clip
|
||||
secsPerTick = 1.0f / RAW_ANIM->GetTicksPerSecond();
|
||||
|
||||
// Start playback
|
||||
Play();
|
||||
|
||||
// Set to initial pose
|
||||
if (rig && rig->GetRootNode())
|
||||
{
|
||||
updateCurrentAnimatorState(currClip, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SHAnimatorComponent::PlayOneShot(Handle<SHAnimationClip> clip)
|
||||
|
@ -67,6 +100,37 @@ namespace SHADE
|
|||
currPlaybackTime = 0.0f;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Update Functions */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void SHAnimatorComponent::Update(float dt)
|
||||
{
|
||||
// Reset matrices
|
||||
std::fill(boneMatrices.begin(), boneMatrices.end(), SHMatrix::Identity);
|
||||
|
||||
// Do not do anything if is not playing or there's nothing to animate
|
||||
if (!isPlaying || !rig || !rig->GetRootNode())
|
||||
return;
|
||||
|
||||
// Update the animation controller if any, this will set the currClip
|
||||
if (animController)
|
||||
{
|
||||
updateAnimController(dt);
|
||||
|
||||
// Only update the animation state if there is a clip
|
||||
if (animInstanceData.CurrentNode && animInstanceData.CurrentNode->Clip)
|
||||
{
|
||||
updateCurrentAnimatorState(animInstanceData.CurrentNode->Clip, animInstanceData.ClipPlaybackTime);
|
||||
}
|
||||
}
|
||||
// Otherwise, a single clip was provided, then we'll use it
|
||||
else if (currClip)
|
||||
{
|
||||
updateManualClipState(dt);
|
||||
updateCurrentAnimatorState(currClip, currPlaybackTime);
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Setter Functions */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
@ -86,95 +150,133 @@ namespace SHADE
|
|||
}
|
||||
}
|
||||
|
||||
void SHAnimatorComponent::SetClip(Handle<SHAnimationClip> newClip)
|
||||
void SHAnimatorComponent::SetAnimationController(Handle<SHAnimationController> ac)
|
||||
{
|
||||
// No change
|
||||
if (currClip == newClip)
|
||||
if (animController == ac)
|
||||
return;
|
||||
|
||||
// Get animation data
|
||||
auto animData = currClip->GetRawAnimation();
|
||||
if (!animData)
|
||||
return;
|
||||
// Set the controller
|
||||
animController = ac;
|
||||
|
||||
// Set parameters
|
||||
currClip = newClip;
|
||||
secsPerTick = 1.0f / animData->GetTicksPerSecond();
|
||||
|
||||
// Calculate total time for the clip
|
||||
if (currClip)
|
||||
// If valid, we want to initialize it
|
||||
if (animController)
|
||||
{
|
||||
const int ONE_PAST_LAST_FRAME = currClip->GetEndFrameIndex() + 1;
|
||||
currClipTotalTime = (ONE_PAST_LAST_FRAME - currClip->GetStartFrameIndex()) / animData->GetTicksPerSecond();
|
||||
// Parameters
|
||||
animInstanceData.Params.clear();
|
||||
for (auto param : animController->GetParams())
|
||||
{
|
||||
animInstanceData.Params.emplace(param.first, SHAnimationController::AnimParam(param.second));
|
||||
}
|
||||
// First Node
|
||||
animInstanceData.CurrentNode = animController->GetStartingNode();
|
||||
// Playback Time
|
||||
animInstanceData.ClipPlaybackTime = 0.0f;
|
||||
|
||||
// Get set of unique SHRawAnimation used by the animController
|
||||
std::unordered_set<Handle<SHRawAnimation>> rawAnims;
|
||||
for (auto node : animController->GetNodes())
|
||||
{
|
||||
// Ensure no null handles
|
||||
if (!node)
|
||||
continue;
|
||||
const Handle<SHAnimationClip> CLIP = node->Clip;
|
||||
if (!CLIP)
|
||||
continue;
|
||||
const Handle<SHRawAnimation> RAW_ANIM = CLIP->GetRawAnimation();
|
||||
if (!RAW_ANIM)
|
||||
continue;
|
||||
|
||||
// Store
|
||||
rawAnims.emplace(RAW_ANIM);
|
||||
}
|
||||
|
||||
// Build channel map and clip-specific data
|
||||
channelMap.clear();
|
||||
if (animData)
|
||||
// Load channel maps
|
||||
for (auto anim : rawAnims)
|
||||
{
|
||||
for (const auto& channel : animData->GetChannels())
|
||||
channelMaps.emplace(anim, createChannelMap(anim));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Helper Functions - Loading */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
SHAnimatorComponent::ChannelMap SHAnimatorComponent::createChannelMap(Handle<SHRawAnimation> rawAnimData)
|
||||
{
|
||||
ChannelMap channelMap;
|
||||
for (const auto& channel : rawAnimData->GetChannels())
|
||||
{
|
||||
channelMap.emplace(channel.Name, &channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (rig && rig->GetRootNode() && currClip)
|
||||
{
|
||||
updatePoseWithClip(0.0f);
|
||||
}
|
||||
return channelMap;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Update Functions */
|
||||
/* Helper Functions - Update */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void SHAnimatorComponent::Update(float dt)
|
||||
void SHAnimatorComponent::updateAnimController(float dt)
|
||||
{
|
||||
// Reset matrices
|
||||
std::fill(boneMatrices.begin(), boneMatrices.end(), SHMatrix::Identity);
|
||||
|
||||
// Nothing to animate
|
||||
if (!currClip || !isPlaying || !rig || !rig->GetRootNode())
|
||||
// No animation controller
|
||||
if (!animInstanceData.CurrentNode)
|
||||
return;
|
||||
|
||||
// Get animation data
|
||||
auto animData = currClip->GetRawAnimation();
|
||||
if (!animData)
|
||||
return;
|
||||
// Update the animation controller
|
||||
animController->Update(animInstanceData, dt);
|
||||
|
||||
// Update time on the playback
|
||||
// Get current clip
|
||||
currClip = animInstanceData.CurrentNode->Clip;
|
||||
if (currClip && currClip->GetRawAnimation())
|
||||
{
|
||||
secsPerTick = 1.0f / currClip->GetRawAnimation();
|
||||
}
|
||||
}
|
||||
void SHAnimatorComponent::updateManualClipState(float dt)
|
||||
{
|
||||
currPlaybackTime += dt;
|
||||
if (currPlaybackTime > currClipTotalTime)
|
||||
if (currPlaybackTime > currClip->GetTotalDuration())
|
||||
{
|
||||
if (playOnce)
|
||||
{
|
||||
playOnce = false;
|
||||
isPlaying = false;
|
||||
currPlaybackTime = currClip->GetTotalDuration();
|
||||
}
|
||||
else
|
||||
{
|
||||
currPlaybackTime = currPlaybackTime - currClipTotalTime;
|
||||
currPlaybackTime = currPlaybackTime - currClip->GetTotalDuration();
|
||||
}
|
||||
}
|
||||
}
|
||||
void SHAnimatorComponent::updateCurrentAnimatorState(Handle<SHAnimationClip> clip, float playbackTime)
|
||||
{
|
||||
// Nothing to animate
|
||||
if (!clip || !isPlaying || !rig || !rig->GetRootNode())
|
||||
return;
|
||||
|
||||
// Check that we have animation data
|
||||
if (!clip->GetRawAnimation())
|
||||
return;
|
||||
|
||||
// Play the clip
|
||||
updatePoseWithClip(currPlaybackTime);
|
||||
updatePoseWithClip(clip, currPlaybackTime);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void SHAnimatorComponent::updatePoseWithClip(float poseTime)
|
||||
void SHAnimatorComponent::updatePoseWithClip(Handle<SHAnimationClip> clip, float poseTime)
|
||||
{
|
||||
// Get closest frame index
|
||||
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);
|
||||
const int CLOSEST_FRAME_IDX = clip->GetStartFrameIndex() + static_cast<int>(std::floorf(poseTime / secsPerTick));
|
||||
updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, clip->GetRawAnimation(), rig->GetRootNode(), SHMatrix::Identity);
|
||||
}
|
||||
|
||||
void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix)
|
||||
void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRawAnimation> rawAnimData, Handle<SHRigNode> node, const SHMatrix& parentMatrix)
|
||||
{
|
||||
// Check if there is a channel for this node
|
||||
const std::string& BONE_NAME = rig->GetName(node);
|
||||
SHMatrix transformMatrix = node->TransformMatrix;
|
||||
|
||||
if (channelMaps.contains(rawAnimData))
|
||||
{
|
||||
auto channelMap = channelMaps[rawAnimData];
|
||||
if (channelMap.contains(BONE_NAME))
|
||||
{
|
||||
const auto CHANNEL = channelMap[BONE_NAME];
|
||||
|
@ -185,6 +287,7 @@ namespace SHADE
|
|||
getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply parent's transformation
|
||||
transformMatrix = transformMatrix * parentMatrix;
|
||||
|
@ -200,7 +303,7 @@ namespace SHADE
|
|||
// Apply pose to children
|
||||
for (auto& child : node->Children)
|
||||
{
|
||||
updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix);
|
||||
updatePoseWithClip(closestFrameIndex, poseTime, rawAnimData, child, transformMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "Math/Vector/SHVec3.h"
|
||||
#include "Math/SHQuaternion.h"
|
||||
#include "SHRawAnimation.h"
|
||||
#include "SHAnimationController.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
@ -52,12 +53,14 @@ namespace SHADE
|
|||
/// </summary>
|
||||
void Play();
|
||||
/// <summary>
|
||||
/// Plays the specified animation clip from the start.
|
||||
/// Plays the specified animation clip from the start. This will unset any
|
||||
/// SHAnimationControllers that have been set.
|
||||
/// </summary>
|
||||
/// <param name="clip">Animation clip to play.</param>
|
||||
void Play(Handle<SHAnimationClip> clip);
|
||||
/// <summary>
|
||||
/// Plays the specified animation clip from the start one time only.
|
||||
/// Plays the specified animation clip from the start one time only. This will unset
|
||||
/// any SHAnimationControllers that have been set.
|
||||
/// </summary>
|
||||
/// <param name="clip">Animation clip to play.</param>
|
||||
void PlayOneShot(Handle<SHAnimationClip> clip);
|
||||
|
@ -74,6 +77,16 @@ namespace SHADE
|
|||
/// </summary>
|
||||
void Stop();
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Update Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Updates the current state of the animation if one is specified based on the
|
||||
/// current animation clip and frames. This will update the bone matrices.
|
||||
/// </summary>
|
||||
/// <param name="dt">Time passed since the last frame.</param>
|
||||
void Update(float dt);
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Setter Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
|
@ -82,13 +95,7 @@ namespace SHADE
|
|||
/// </summary>
|
||||
/// <param name="newRig">Rig to use.</param>
|
||||
void SetRig(Handle<SHRig> newRig);
|
||||
/// <summary>
|
||||
/// Sets the animation clip of this animator without playing it.
|
||||
/// This will set the pose of the model to it's initial pose.
|
||||
/// If the clip is the same as the current clip, nothing happens.
|
||||
/// </summary>
|
||||
/// <param name="newClip">Clip to use.</param>
|
||||
void SetClip(Handle<SHAnimationClip> newClip);
|
||||
void SetAnimationController(Handle<SHAnimationController> ac);
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Getter Functions */
|
||||
|
@ -104,51 +111,55 @@ namespace SHADE
|
|||
/// <returns>Handle to the currently set rig.</returns>
|
||||
Handle<SHRig> GetRig() const noexcept { return rig; }
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Retrieve the currently set animation clip.
|
||||
/// </summary>
|
||||
/// <returns>Handle to the currently set animation clip.</returns>
|
||||
Handle<SHAnimationClip> GetCurrentClip() const noexcept { return currClip; }
|
||||
/// <summary>
|
||||
/// Checks if an animation is currently playing.
|
||||
/// </summary>
|
||||
/// <returns>True if an animation clip is currently playing.</returns>
|
||||
bool IsPlaying() const { return isPlaying; }
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Update Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Updates the current state of the animation if one is specified based on the
|
||||
/// current animation clip and frames. This will update the bone matrices.
|
||||
/// Retrieves the current node for the Animation Controller. This returns a null
|
||||
/// if there is no Animation Controller currently set.
|
||||
/// </summary>
|
||||
/// <param name="dt">Time passed since the last frame.</param>
|
||||
void Update(float dt);
|
||||
/// <returns>Handle to the current Animation Controller node.</returns>
|
||||
Handle<SHAnimationController::Node> GetCurrentNode() const noexcept { return animInstanceData.CurrentNode; }
|
||||
Handle<SHAnimationController> GetAnimationController() const noexcept { return animController; }
|
||||
|
||||
private:
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Type Definition */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
using ChannelMap = std::unordered_map<std::string, const SHRawAnimation::Channel*>;
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Data Members */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
// Resources
|
||||
Handle<SHRig> rig;
|
||||
// Playback Tracking for Animation Controller Mode
|
||||
Handle<SHAnimationController> animController;
|
||||
SHAnimationController::InstanceData animInstanceData;
|
||||
// Playback Tracking for Manual Mode
|
||||
Handle<SHAnimationClip> currClip;
|
||||
// Playback Tracking
|
||||
float currPlaybackTime = 0.0f;
|
||||
bool isPlaying = true;
|
||||
bool playOnce = false;
|
||||
float currClipTotalTime = 0.0f;
|
||||
// Useful Cached Data
|
||||
// Shared Tracking
|
||||
bool isPlaying = true;
|
||||
float secsPerTick = 0.0f;
|
||||
// Buffer
|
||||
std::vector<SHMatrix> boneMatrices;
|
||||
// Caches
|
||||
std::unordered_map<std::string, const SHRawAnimation::Channel*> channelMap;
|
||||
std::unordered_map<Handle<SHRawAnimation>, ChannelMap> channelMaps;
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void updatePoseWithClip(float poseTime);
|
||||
void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix);
|
||||
// Loading
|
||||
ChannelMap createChannelMap(Handle<SHRawAnimation> rawAnimData);
|
||||
// Update
|
||||
void updateAnimController(float dt);
|
||||
void updateManualClipState(float dt);
|
||||
void updateCurrentAnimatorState(Handle<SHAnimationClip> clip, float playbackTime);
|
||||
void updatePoseWithClip(Handle<SHAnimationClip> clip, float poseTime);
|
||||
void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRawAnimation> rawAnimData, Handle<SHRigNode> node, const SHMatrix& parentMatrix);
|
||||
template<typename T>
|
||||
T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime);
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/************************************************************************************//*!
|
||||
\file SHAnimControllerAsset.h
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Mar 1, 2023
|
||||
\brief Contains the definition of the SHAnimControllerAsset 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 SHAnimControllerAsset : SHAssetData
|
||||
{
|
||||
std::string name;
|
||||
// TODO
|
||||
};
|
||||
}
|
|
@ -646,31 +646,6 @@ namespace SHADE
|
|||
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID);
|
||||
}
|
||||
}
|
||||
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() ? component->GetCurrentClip()->GetRawAnimation() : Handle<SHRawAnimation>{});
|
||||
return SHResourceManager::GetAssetID<SHRawAnimation>(clip).value_or(0);
|
||||
},
|
||||
[component](AssetID const& id)
|
||||
{
|
||||
if (SHAssetManager::GetType(id) != AssetType::MODEL)
|
||||
{
|
||||
SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!")
|
||||
return;
|
||||
}
|
||||
component->SetClip(SHResourceManager::LoadOrGet<SHAnimationClip>(id));
|
||||
}, SHDragDrop::DRAG_RESOURCE);
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
{
|
||||
if (Handle<SHRawAnimation> const clip = component->GetCurrentClip()->GetRawAnimation())
|
||||
{
|
||||
AssetID assetID = SHResourceManager::GetAssetID<SHRawAnimation>(clip).value_or(0);
|
||||
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@ of DigiPen Institute of Technology is prohibited.
|
|||
#include "Assets/Asset Types/SHTextureAsset.h"
|
||||
#include "Assets/Asset Types/SHShaderAsset.h"
|
||||
#include "Assets/Asset Types/SHAnimClipAsset.h"
|
||||
#include "Assets/Asset Types/SHAnimControllerAsset.h"
|
||||
#include "Graphics/Shaders/SHVkShaderModule.h"
|
||||
#include "Graphics/MiddleEnd/Textures/SHTextureLibrary.h"
|
||||
#include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h"
|
||||
|
@ -40,6 +41,7 @@ namespace SHADE
|
|||
class SHMaterial;
|
||||
struct SHRigNode;
|
||||
class SHAnimationClip;
|
||||
class SHAnimationController;
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Type Definitions */
|
||||
|
@ -58,6 +60,7 @@ namespace SHADE
|
|||
template<> struct SHResourceLoader<SHRawAnimation> { using AssetType = SHModelAsset; };
|
||||
template<> struct SHResourceLoader<SHRig> { using AssetType = SHModelAsset; };
|
||||
template<> struct SHResourceLoader<SHAnimationClip> { using AssetType = SHAnimClipAsset; };
|
||||
template<> struct SHResourceLoader<SHAnimationController> { using AssetType = SHAnimControllerAsset; };
|
||||
|
||||
/// <summary>
|
||||
/// Static class responsible for loading and caching runtime resources from their
|
||||
|
|
|
@ -43,7 +43,9 @@ namespace SHADE
|
|||
!std::is_same_v<ResourceType, SHFont> &&
|
||||
!std::is_same_v<ResourceType, SHMaterial> &&
|
||||
!std::is_same_v<ResourceType, SHRawAnimation> &&
|
||||
!std::is_same_v<ResourceType, SHRig>
|
||||
!std::is_same_v<ResourceType, SHRig> &&
|
||||
!std::is_same_v<ResourceType, SHAnimationClip> &&
|
||||
!std::is_same_v<ResourceType, SHAnimationController>
|
||||
)
|
||||
{
|
||||
static_assert(true, "Unsupported Resource Type specified for SHResourceManager.");
|
||||
|
@ -361,7 +363,7 @@ namespace SHADE
|
|||
loadedAssetData.emplace_back(assetId);
|
||||
return resourceHub.Create<ResourceType>(*assetData.anims[0]);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ResourceType, SHAnimClipAsset>)
|
||||
else if constexpr (std::is_same_v<ResourceType, SHAnimationClip>)
|
||||
{
|
||||
loadedAssetData.emplace_back(assetId);
|
||||
return resourceHub.Create<ResourceType>
|
||||
|
@ -371,5 +373,13 @@ namespace SHADE
|
|||
assetData->lastIndex
|
||||
);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ResourceType, SHAnimationController>)
|
||||
{
|
||||
loadedAssetData.emplace_back(assetId);
|
||||
return resourceHub.Create<ResourceType>
|
||||
(
|
||||
// TODO
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -394,13 +394,13 @@ namespace YAML
|
|||
struct convert<SHAnimatorComponent>
|
||||
{
|
||||
static constexpr std::string_view RIG_YAML_TAG = "Rig";
|
||||
static constexpr std::string_view CLIP_YAML_TAG = "Clip";
|
||||
static constexpr std::string_view AC_YAML_TAG = "AnimationController";
|
||||
|
||||
static YAML::Node encode(SHAnimatorComponent const& rhs)
|
||||
{
|
||||
YAML::Node node;
|
||||
node[RIG_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHRig>(rhs.GetRig()).value_or(0);
|
||||
node[CLIP_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHAnimationClip>(rhs.GetCurrentClip()).value_or(0);
|
||||
node[AC_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHAnimationController>(rhs.GetAnimationController()).value_or(0);
|
||||
return node;
|
||||
}
|
||||
static bool decode(YAML::Node const& node, SHAnimatorComponent& rhs)
|
||||
|
@ -409,9 +409,9 @@ namespace YAML
|
|||
{
|
||||
rhs.SetRig(SHResourceManager::LoadOrGet<SHRig>(node[RIG_YAML_TAG.data()].as<AssetID>()));
|
||||
}
|
||||
if (node[CLIP_YAML_TAG.data()].IsDefined())
|
||||
if (node[AC_YAML_TAG.data()].IsDefined())
|
||||
{
|
||||
rhs.SetClip(SHResourceManager::LoadOrGet<SHAnimationClip>(node[CLIP_YAML_TAG.data()].as<AssetID>()));
|
||||
rhs.SetAnimationController(SHResourceManager::LoadOrGet<SHAnimationController>(node[AC_YAML_TAG.data()].as<AssetID>()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue