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
|
||||
{
|
||||
|
@ -20,8 +21,15 @@ namespace SHADE
|
|||
/* Constructors */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
SHAnimationClip::SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame)
|
||||
: rawAnim { rawAnimHandle }
|
||||
: 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,22 +20,28 @@ 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;
|
||||
|
||||
// Check if we finished playing
|
||||
if (instData.ClipPlaybackTime > instData.CurrentNode->Clip->GetTotalDuration())
|
||||
{
|
||||
// Clamp
|
||||
instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetTotalDuration();
|
||||
|
||||
// Go to next state
|
||||
for (const auto& transition : instData.CurrentNode->Transitions)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
void SHAnimationController::Reset()
|
||||
{
|
||||
currentNode = startNode;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Usage Functions */
|
||||
|
@ -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;
|
||||
Type ParamType;
|
||||
ValueType Value;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -91,10 +103,11 @@ namespace SHADE
|
|||
/*-------------------------------------------------------------------------------*/
|
||||
/* Data Members */
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
ConditionType Condition;
|
||||
Handle<Node> Target;
|
||||
ConditionType Condition;
|
||||
AnimParam::ValueType Threshold;
|
||||
Handle<Node> Target;
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Describes a node in the animation controller.
|
||||
/// </summary>
|
||||
|
@ -103,6 +116,16 @@ namespace SHADE
|
|||
Handle<SHAnimationClip> Clip;
|
||||
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 */
|
||||
|
@ -110,11 +133,7 @@ namespace SHADE
|
|||
/// <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;
|
||||
Play();
|
||||
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,104 +150,143 @@ 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();
|
||||
}
|
||||
|
||||
// Build channel map and clip-specific data
|
||||
channelMap.clear();
|
||||
if (animData)
|
||||
{
|
||||
for (const auto& channel : animData->GetChannels())
|
||||
// Parameters
|
||||
animInstanceData.Params.clear();
|
||||
for (auto param : animController->GetParams())
|
||||
{
|
||||
channelMap.emplace(channel.Name, &channel);
|
||||
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);
|
||||
}
|
||||
|
||||
// Load channel maps
|
||||
for (auto anim : rawAnims)
|
||||
{
|
||||
channelMaps.emplace(anim, createChannelMap(anim));
|
||||
}
|
||||
}
|
||||
|
||||
if (rig && rig->GetRootNode() && currClip)
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Helper Functions - Loading */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
SHAnimatorComponent::ChannelMap SHAnimatorComponent::createChannelMap(Handle<SHRawAnimation> rawAnimData)
|
||||
{
|
||||
ChannelMap channelMap;
|
||||
for (const auto& channel : rawAnimData->GetChannels())
|
||||
{
|
||||
updatePoseWithClip(0.0f);
|
||||
channelMap.emplace(channel.Name, &channel);
|
||||
}
|
||||
return channelMap;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Helper Functions - Update */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void SHAnimatorComponent::updateAnimController(float dt)
|
||||
{
|
||||
// No animation controller
|
||||
if (!animInstanceData.CurrentNode)
|
||||
return;
|
||||
|
||||
// Update the animation controller
|
||||
animController->Update(animInstanceData, dt);
|
||||
|
||||
// Get current clip
|
||||
currClip = animInstanceData.CurrentNode->Clip;
|
||||
if (currClip && currClip->GetRawAnimation())
|
||||
{
|
||||
secsPerTick = 1.0f / currClip->GetRawAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* Update Functions */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void SHAnimatorComponent::Update(float dt)
|
||||
void SHAnimatorComponent::updateManualClipState(float dt)
|
||||
{
|
||||
// Reset matrices
|
||||
std::fill(boneMatrices.begin(), boneMatrices.end(), SHMatrix::Identity);
|
||||
|
||||
// Nothing to animate
|
||||
if (!currClip || !isPlaying || !rig || !rig->GetRootNode())
|
||||
return;
|
||||
|
||||
// Get animation data
|
||||
auto animData = currClip->GetRawAnimation();
|
||||
if (!animData)
|
||||
return;
|
||||
|
||||
// Update time on the playback
|
||||
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 (channelMap.contains(BONE_NAME))
|
||||
|
||||
if (channelMaps.contains(rawAnimData))
|
||||
{
|
||||
const auto CHANNEL = channelMap[BONE_NAME];
|
||||
transformMatrix = SHMatrix::Transform
|
||||
(
|
||||
getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime),
|
||||
getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime),
|
||||
getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime)
|
||||
);
|
||||
auto channelMap = channelMaps[rawAnimData];
|
||||
if (channelMap.contains(BONE_NAME))
|
||||
{
|
||||
const auto CHANNEL = channelMap[BONE_NAME];
|
||||
transformMatrix = SHMatrix::Transform
|
||||
(
|
||||
getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime),
|
||||
getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime),
|
||||
getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply parent's transformation
|
||||
|
@ -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
|
||||
};
|
||||
}
|
|
@ -18,10 +18,10 @@
|
|||
// FMOD Fwd Declare
|
||||
namespace FMOD
|
||||
{
|
||||
class Sound;
|
||||
class System;
|
||||
class ChannelGroup;
|
||||
class Channel;
|
||||
class Sound;
|
||||
class System;
|
||||
class ChannelGroup;
|
||||
class Channel;
|
||||
}
|
||||
enum FMOD_RESULT : int;
|
||||
enum FMOD_SPEAKERMODE : int;
|
||||
|
@ -45,9 +45,9 @@ constexpr AssetID INVALID_ASSET_ID {0};
|
|||
// Asset type enum
|
||||
enum class AssetType : AssetTypeMeta
|
||||
{
|
||||
INVALID,
|
||||
SHADER,
|
||||
SHADER_BUILT_IN,
|
||||
INVALID,
|
||||
SHADER,
|
||||
SHADER_BUILT_IN,
|
||||
TEXTURE,
|
||||
MODEL,
|
||||
SCENE,
|
||||
|
@ -56,7 +56,7 @@ enum class AssetType : AssetTypeMeta
|
|||
MESH,
|
||||
SCRIPT,
|
||||
FONT,
|
||||
MAX_COUNT
|
||||
MAX_COUNT
|
||||
};
|
||||
constexpr size_t TYPE_COUNT{ static_cast<size_t>(AssetType::MAX_COUNT) };
|
||||
|
||||
|
@ -94,18 +94,18 @@ constexpr std::string_view TEXTURE_EXTENSION {".shtex"};
|
|||
constexpr std::string_view MODEL_EXTENSION{ ".shmodel" };
|
||||
|
||||
constexpr std::string_view EXTENSIONS[] = {
|
||||
AUDIO_EXTENSION,
|
||||
SHADER_EXTENSION,
|
||||
SHADER_BUILT_IN_EXTENSION,
|
||||
AUDIO_EXTENSION,
|
||||
SHADER_EXTENSION,
|
||||
SHADER_BUILT_IN_EXTENSION,
|
||||
TEXTURE_EXTENSION,
|
||||
MODEL_EXTENSION,
|
||||
SCENE_EXTENSION,
|
||||
PREFAB_EXTENSION,
|
||||
PREFAB_EXTENSION,
|
||||
MATERIAL_EXTENSION,
|
||||
"dummy",
|
||||
SCRIPT_EXTENSION,
|
||||
FONT_EXTENSION,
|
||||
AUDIO_WAV_EXTENSION,
|
||||
AUDIO_WAV_EXTENSION,
|
||||
};
|
||||
|
||||
constexpr size_t EXTENSIONS_COUNT{ 11 };
|
||||
|
@ -118,10 +118,10 @@ constexpr std::string_view GLTF_EXTENSION{ ".gltf" };
|
|||
constexpr std::string_view TTF_EXTENSION{ ".ttf" };
|
||||
|
||||
constexpr std::string_view EXTERNALS[] = {
|
||||
GLSL_EXTENSION,
|
||||
DDS_EXTENSION,
|
||||
FBX_EXTENSION,
|
||||
GLTF_EXTENSION,
|
||||
GLSL_EXTENSION,
|
||||
DDS_EXTENSION,
|
||||
FBX_EXTENSION,
|
||||
GLTF_EXTENSION,
|
||||
TTF_EXTENSION
|
||||
};
|
||||
|
||||
|
@ -131,9 +131,9 @@ constexpr std::string_view FRAGMENT_SHADER{ "_FS" };
|
|||
constexpr std::string_view COMPUTER_SHADER{ "_CS" };
|
||||
|
||||
constexpr std::pair<std::string_view, SHADE::SH_SHADER_TYPE> SHADER_IDENTIFIERS[] = {
|
||||
std::make_pair(VERTEX_SHADER, SHADE::SH_SHADER_TYPE::VERTEX),
|
||||
std::make_pair(FRAGMENT_SHADER, SHADE::SH_SHADER_TYPE::FRAGMENT),
|
||||
std::make_pair(COMPUTER_SHADER, SHADE::SH_SHADER_TYPE::COMPUTE)
|
||||
std::make_pair(VERTEX_SHADER, SHADE::SH_SHADER_TYPE::VERTEX),
|
||||
std::make_pair(FRAGMENT_SHADER, SHADE::SH_SHADER_TYPE::FRAGMENT),
|
||||
std::make_pair(COMPUTER_SHADER, SHADE::SH_SHADER_TYPE::COMPUTE)
|
||||
};
|
||||
|
||||
constexpr size_t SHADER_TYPE_MAX_COUNT{ 3 };
|
||||
|
|
|
@ -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 */
|
||||
|
@ -48,16 +50,17 @@ namespace SHADE
|
|||
/// Template structs that maps a resource to their loaded asset representation type.
|
||||
/// </summary>
|
||||
template<typename T = void>
|
||||
struct SHResourceLoader { using AssetType = void; };
|
||||
template<> struct SHResourceLoader<SHMesh> { using AssetType = SHMeshAsset; };
|
||||
template<> struct SHResourceLoader<SHTexture> { using AssetType = SHTextureAsset; };
|
||||
template<> struct SHResourceLoader<SHVkShaderModule> { using AssetType = SHShaderAsset; };
|
||||
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<SHAnimationClip> { using AssetType = SHAnimClipAsset; };
|
||||
struct SHResourceLoader { using AssetType = void; };
|
||||
template<> struct SHResourceLoader<SHMesh> { using AssetType = SHMeshAsset; };
|
||||
template<> struct SHResourceLoader<SHTexture> { using AssetType = SHTextureAsset; };
|
||||
template<> struct SHResourceLoader<SHVkShaderModule> { using AssetType = SHShaderAsset; };
|
||||
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<SHAnimationClip> { using AssetType = SHAnimClipAsset; };
|
||||
template<> struct SHResourceLoader<SHAnimationController> { using AssetType = SHAnimControllerAsset; };
|
||||
|
||||
/// <summary>
|
||||
/// Static class responsible for loading and caching runtime resources from their
|
||||
|
|
|
@ -36,14 +36,16 @@ namespace SHADE
|
|||
Handle<ResourceType> SHResourceManager::LoadOrGet(AssetID assetId)
|
||||
{
|
||||
// Check if it is an unsupported type
|
||||
if (!std::is_same_v<ResourceType, SHMesh> &&
|
||||
!std::is_same_v<ResourceType, SHTexture> &&
|
||||
!std::is_same_v<ResourceType, SHVkShaderModule> &&
|
||||
!std::is_same_v<ResourceType, SHMaterialSpec> &&
|
||||
!std::is_same_v<ResourceType, SHFont> &&
|
||||
!std::is_same_v<ResourceType, SHMaterial> &&
|
||||
!std::is_same_v<ResourceType, SHRawAnimation> &&
|
||||
!std::is_same_v<ResourceType, SHRig>
|
||||
if (!std::is_same_v<ResourceType, SHMesh> &&
|
||||
!std::is_same_v<ResourceType, SHTexture> &&
|
||||
!std::is_same_v<ResourceType, SHVkShaderModule> &&
|
||||
!std::is_same_v<ResourceType, SHMaterialSpec> &&
|
||||
!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, 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