Modified SHAnimatorComponent to support manual and animation controller modes. Fleshed out SHAnimationController. Added SHAnimControllerAsset stub.

This commit is contained in:
Kah Wei 2023-03-01 01:30:34 +08:00
parent 36af939c67
commit e97f5747cb
12 changed files with 374 additions and 187 deletions

View File

@ -13,6 +13,7 @@ of DigiPen Institute of Technology is prohibited.
#include "SHpch.h" #include "SHpch.h"
// Primary Header // Primary Header
#include "SHAnimationClip.h" #include "SHAnimationClip.h"
#include "SHRawAnimation.h"
namespace SHADE namespace SHADE
{ {
@ -20,8 +21,15 @@ namespace SHADE
/* Constructors */ /* Constructors */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHAnimationClip::SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame) SHAnimationClip::SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame)
: rawAnim { rawAnimHandle } : rawAnim { rawAnimHandle }
, startFrameIndex { firstFrame } , startFrameIndex { firstFrame }
, endFrameIndex { lastFrame } , endFrameIndex { lastFrame }
{} , duration { 0.0f }
{
if (!rawAnim)
return;
const int ONE_PAST_LAST_FRAME = lastFrame + 1;
duration = (ONE_PAST_LAST_FRAME - firstFrame) / rawAnim->GetTicksPerSecond();
}
} }

View File

@ -50,6 +50,7 @@ namespace SHADE
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; }
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -58,5 +59,6 @@ namespace SHADE
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
}; };
} }

View File

@ -20,22 +20,28 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Lifecycle Functions */ /* Lifecycle Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHAnimationController::Update() void SHAnimationController::Update(InstanceData& instData, float dt)
{ {
// Is there a valid node // Is there a valid node
if (!currentNode) if (!instData.CurrentNode)
return; return;
// Update // Update the current playback
for (const auto& transition : currentNode->Transitions) 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 */ /* Usage Functions */
@ -75,4 +81,24 @@ namespace SHADE
// Clear node // Clear node
node.Free(); 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;
}
}
} }

View File

@ -27,6 +27,7 @@ namespace SHADE
/// <summary> /// <summary>
/// Object that controls the animation that is played by an animator through the use /// Object that controls the animation that is played by an animator through the use
/// of an internal state machine. /// of an internal state machine.
/// This should never be modified once it has been attached to a SHAnimatorComponent!
/// </summary> /// </summary>
class SH_API SHAnimationController class SH_API SHAnimationController
{ {
@ -58,12 +59,23 @@ namespace SHADE
Float, Float,
Int 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 */ /* Data Members */
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
Type ParamType; Type ParamType;
std::variant<bool, float, int> Value; ValueType Value;
}; };
/// <summary> /// <summary>
@ -91,8 +103,9 @@ namespace SHADE
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
ConditionType Condition; ConditionType Condition;
Handle<Node> Target; AnimParam::ValueType Threshold;
Handle<Node> Target;
}; };
/// <summary> /// <summary>
@ -104,17 +117,23 @@ namespace SHADE
std::vector<Transition> Transitions; 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 */ /* Lifecycle Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/// <summary> /// <summary>
/// Runs a single update for the animation controller. /// Runs a single update for the animation controller.
/// </summary> /// </summary>
void Update(); void Update(InstanceData& instData, float dt);
/// <summary>
/// Resets the animation controller to its starting node.
/// </summary>
void Reset();
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Usage Functions */ /* Usage Functions */
@ -130,20 +149,23 @@ namespace SHADE
/// </summary> /// </summary>
/// <param name="node">Node to destroy.</param> /// <param name="node">Node to destroy.</param>
void DestroyNode(Handle<Node> node); void DestroyNode(Handle<Node> node);
void AddTransition(Handle<Node> source, const Transition& transition);
void AddParameter(const std::string& name, AnimParam::Type type);
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getters */ /* 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: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
// State machine // State machine
Handle<Node> currentNode;
Handle<Node> startNode; Handle<Node> startNode;
std::vector<Handle<Node>> nodes; std::vector<Handle<Node>> nodes;
std::unordered_map<std::string, AnimParam> parameters; std::unordered_map<std::string, AnimParam::Type> parameters;
}; };
} }

View File

@ -39,9 +39,42 @@ namespace SHADE
void SHAnimatorComponent::Play(Handle<SHAnimationClip> clip) 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; currClip = clip;
currPlaybackTime = 0.0f; 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) void SHAnimatorComponent::PlayOneShot(Handle<SHAnimationClip> clip)
@ -67,6 +100,37 @@ namespace SHADE
currPlaybackTime = 0.0f; 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 */ /* Setter Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -86,104 +150,143 @@ namespace SHADE
} }
} }
void SHAnimatorComponent::SetClip(Handle<SHAnimationClip> newClip) void SHAnimatorComponent::SetAnimationController(Handle<SHAnimationController> ac)
{ {
// No change // No change
if (currClip == newClip) if (animController == ac)
return; return;
// Get animation data // Set the controller
auto animData = currClip->GetRawAnimation(); animController = ac;
if (!animData)
return;
// Set parameters // If valid, we want to initialize it
currClip = newClip; if (animController)
secsPerTick = 1.0f / animData->GetTicksPerSecond();
// Calculate total time for the clip
if (currClip)
{ {
const int ONE_PAST_LAST_FRAME = currClip->GetEndFrameIndex() + 1; // Parameters
currClipTotalTime = (ONE_PAST_LAST_FRAME - currClip->GetStartFrameIndex()) / animData->GetTicksPerSecond(); animInstanceData.Params.clear();
} for (auto param : animController->GetParams())
// Build channel map and clip-specific data
channelMap.clear();
if (animData)
{
for (const auto& channel : animData->GetChannels())
{ {
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;
if (rig && rig->GetRootNode() && currClip) // Get set of unique SHRawAnimation used by the animController
{ std::unordered_set<Handle<SHRawAnimation>> rawAnims;
updatePoseWithClip(0.0f); 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));
}
} }
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Update Functions */ /* Helper Functions - Loading */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHAnimatorComponent::Update(float dt) SHAnimatorComponent::ChannelMap SHAnimatorComponent::createChannelMap(Handle<SHRawAnimation> rawAnimData)
{ {
// Reset matrices ChannelMap channelMap;
std::fill(boneMatrices.begin(), boneMatrices.end(), SHMatrix::Identity); for (const auto& channel : rawAnimData->GetChannels())
{
channelMap.emplace(channel.Name, &channel);
}
return channelMap;
}
// Nothing to animate /*-----------------------------------------------------------------------------------*/
if (!currClip || !isPlaying || !rig || !rig->GetRootNode()) /* Helper Functions - Update */
/*-----------------------------------------------------------------------------------*/
void SHAnimatorComponent::updateAnimController(float dt)
{
// No animation controller
if (!animInstanceData.CurrentNode)
return; return;
// Get animation data // Update the animation controller
auto animData = currClip->GetRawAnimation(); animController->Update(animInstanceData, dt);
if (!animData)
return;
// 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; currPlaybackTime += dt;
if (currPlaybackTime > currClipTotalTime) if (currPlaybackTime > currClip->GetTotalDuration())
{ {
if (playOnce) if (playOnce)
{ {
playOnce = false; playOnce = false;
isPlaying = false; isPlaying = false;
currPlaybackTime = currClip->GetTotalDuration();
} }
else 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 // Play the clip
updatePoseWithClip(currPlaybackTime); updatePoseWithClip(clip, currPlaybackTime);
} }
void SHAnimatorComponent::updatePoseWithClip(Handle<SHAnimationClip> clip, float poseTime)
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
void SHAnimatorComponent::updatePoseWithClip(float poseTime)
{ {
// Get closest frame index // Get closest frame index
const int CLOSEST_FRAME_IDX = currClip->GetStartFrameIndex() + static_cast<int>(std::floorf(poseTime * currClip->GetRawAnimation()->GetTicksPerSecond())); const int CLOSEST_FRAME_IDX = clip->GetStartFrameIndex() + static_cast<int>(std::floorf(poseTime / secsPerTick));
updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity); 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 // Check if there is a channel for this node
const std::string& BONE_NAME = rig->GetName(node); const std::string& BONE_NAME = rig->GetName(node);
SHMatrix transformMatrix = node->TransformMatrix; SHMatrix transformMatrix = node->TransformMatrix;
if (channelMap.contains(BONE_NAME))
if (channelMaps.contains(rawAnimData))
{ {
const auto CHANNEL = channelMap[BONE_NAME]; auto channelMap = channelMaps[rawAnimData];
transformMatrix = SHMatrix::Transform if (channelMap.contains(BONE_NAME))
( {
getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime), const auto CHANNEL = channelMap[BONE_NAME];
getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime), transformMatrix = SHMatrix::Transform
getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime) (
); getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime),
getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime),
getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime)
);
}
} }
// Apply parent's transformation // Apply parent's transformation
@ -200,7 +303,7 @@ namespace SHADE
// Apply pose to children // Apply pose to children
for (auto& child : node->Children) for (auto& child : node->Children)
{ {
updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix); updatePoseWithClip(closestFrameIndex, poseTime, rawAnimData, child, transformMatrix);
} }
} }
} }

View File

@ -23,6 +23,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Math/Vector/SHVec3.h" #include "Math/Vector/SHVec3.h"
#include "Math/SHQuaternion.h" #include "Math/SHQuaternion.h"
#include "SHRawAnimation.h" #include "SHRawAnimation.h"
#include "SHAnimationController.h"
namespace SHADE namespace SHADE
{ {
@ -52,12 +53,14 @@ namespace SHADE
/// </summary> /// </summary>
void Play(); void Play();
/// <summary> /// <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> /// </summary>
/// <param name="clip">Animation clip to play.</param> /// <param name="clip">Animation clip to play.</param>
void Play(Handle<SHAnimationClip> clip); void Play(Handle<SHAnimationClip> clip);
/// <summary> /// <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> /// </summary>
/// <param name="clip">Animation clip to play.</param> /// <param name="clip">Animation clip to play.</param>
void PlayOneShot(Handle<SHAnimationClip> clip); void PlayOneShot(Handle<SHAnimationClip> clip);
@ -74,6 +77,16 @@ namespace SHADE
/// </summary> /// </summary>
void Stop(); 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 */ /* Setter Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -82,13 +95,7 @@ namespace SHADE
/// </summary> /// </summary>
/// <param name="newRig">Rig to use.</param> /// <param name="newRig">Rig to use.</param>
void SetRig(Handle<SHRig> newRig); void SetRig(Handle<SHRig> newRig);
/// <summary> void SetAnimationController(Handle<SHAnimationController> ac);
/// 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);
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
@ -104,51 +111,55 @@ namespace SHADE
/// <returns>Handle to the currently set rig.</returns> /// <returns>Handle to the currently set rig.</returns>
Handle<SHRig> GetRig() const noexcept { return rig; } Handle<SHRig> GetRig() const noexcept { return rig; }
/// <summary> /// <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. /// Checks if an animation is currently playing.
/// </summary> /// </summary>
/// <returns>True if an animation clip is currently playing.</returns> /// <returns>True if an animation clip is currently playing.</returns>
bool IsPlaying() const { return isPlaying; } bool IsPlaying() const { return isPlaying; }
/*---------------------------------------------------------------------------------*/
/* Update Functions */
/*---------------------------------------------------------------------------------*/
/// <summary> /// <summary>
/// Updates the current state of the animation if one is specified based on the /// Retrieves the current node for the Animation Controller. This returns a null
/// current animation clip and frames. This will update the bone matrices. /// if there is no Animation Controller currently set.
/// </summary> /// </summary>
/// <param name="dt">Time passed since the last frame.</param> /// <returns>Handle to the current Animation Controller node.</returns>
void Update(float dt); Handle<SHAnimationController::Node> GetCurrentNode() const noexcept { return animInstanceData.CurrentNode; }
Handle<SHAnimationController> GetAnimationController() const noexcept { return animController; }
private: private:
/*---------------------------------------------------------------------------------*/
/* Type Definition */
/*---------------------------------------------------------------------------------*/
using ChannelMap = std::unordered_map<std::string, const SHRawAnimation::Channel*>;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
// Resources // Resources
Handle<SHRig> rig; Handle<SHRig> rig;
// Playback Tracking for Animation Controller Mode
Handle<SHAnimationController> animController;
SHAnimationController::InstanceData animInstanceData;
// Playback Tracking for Manual Mode
Handle<SHAnimationClip> currClip; Handle<SHAnimationClip> currClip;
// Playback Tracking
float currPlaybackTime = 0.0f; float currPlaybackTime = 0.0f;
bool isPlaying = true;
bool playOnce = false; bool playOnce = false;
float currClipTotalTime = 0.0f; // Shared Tracking
// Useful Cached Data bool isPlaying = true;
float secsPerTick = 0.0f; float secsPerTick = 0.0f;
// Buffer // Buffer
std::vector<SHMatrix> boneMatrices; std::vector<SHMatrix> boneMatrices;
// Caches // Caches
std::unordered_map<std::string, const SHRawAnimation::Channel*> channelMap; std::unordered_map<Handle<SHRawAnimation>, ChannelMap> channelMaps;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
void updatePoseWithClip(float poseTime); // Loading
void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix); 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> template<typename T>
T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime); T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime);

View File

@ -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
};
}

View File

@ -18,10 +18,10 @@
// FMOD Fwd Declare // FMOD Fwd Declare
namespace FMOD namespace FMOD
{ {
class Sound; class Sound;
class System; class System;
class ChannelGroup; class ChannelGroup;
class Channel; class Channel;
} }
enum FMOD_RESULT : int; enum FMOD_RESULT : int;
enum FMOD_SPEAKERMODE : int; enum FMOD_SPEAKERMODE : int;
@ -45,9 +45,9 @@ constexpr AssetID INVALID_ASSET_ID {0};
// Asset type enum // Asset type enum
enum class AssetType : AssetTypeMeta enum class AssetType : AssetTypeMeta
{ {
INVALID, INVALID,
SHADER, SHADER,
SHADER_BUILT_IN, SHADER_BUILT_IN,
TEXTURE, TEXTURE,
MODEL, MODEL,
SCENE, SCENE,
@ -56,7 +56,7 @@ enum class AssetType : AssetTypeMeta
MESH, MESH,
SCRIPT, SCRIPT,
FONT, FONT,
MAX_COUNT MAX_COUNT
}; };
constexpr size_t TYPE_COUNT{ static_cast<size_t>(AssetType::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 MODEL_EXTENSION{ ".shmodel" };
constexpr std::string_view EXTENSIONS[] = { constexpr std::string_view EXTENSIONS[] = {
AUDIO_EXTENSION, AUDIO_EXTENSION,
SHADER_EXTENSION, SHADER_EXTENSION,
SHADER_BUILT_IN_EXTENSION, SHADER_BUILT_IN_EXTENSION,
TEXTURE_EXTENSION, TEXTURE_EXTENSION,
MODEL_EXTENSION, MODEL_EXTENSION,
SCENE_EXTENSION, SCENE_EXTENSION,
PREFAB_EXTENSION, PREFAB_EXTENSION,
MATERIAL_EXTENSION, MATERIAL_EXTENSION,
"dummy", "dummy",
SCRIPT_EXTENSION, SCRIPT_EXTENSION,
FONT_EXTENSION, FONT_EXTENSION,
AUDIO_WAV_EXTENSION, AUDIO_WAV_EXTENSION,
}; };
constexpr size_t EXTENSIONS_COUNT{ 11 }; 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 TTF_EXTENSION{ ".ttf" };
constexpr std::string_view EXTERNALS[] = { constexpr std::string_view EXTERNALS[] = {
GLSL_EXTENSION, GLSL_EXTENSION,
DDS_EXTENSION, DDS_EXTENSION,
FBX_EXTENSION, FBX_EXTENSION,
GLTF_EXTENSION, GLTF_EXTENSION,
TTF_EXTENSION TTF_EXTENSION
}; };
@ -131,9 +131,9 @@ constexpr std::string_view FRAGMENT_SHADER{ "_FS" };
constexpr std::string_view COMPUTER_SHADER{ "_CS" }; constexpr std::string_view COMPUTER_SHADER{ "_CS" };
constexpr std::pair<std::string_view, SHADE::SH_SHADER_TYPE> SHADER_IDENTIFIERS[] = { 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(VERTEX_SHADER, SHADE::SH_SHADER_TYPE::VERTEX),
std::make_pair(FRAGMENT_SHADER, SHADE::SH_SHADER_TYPE::FRAGMENT), std::make_pair(FRAGMENT_SHADER, SHADE::SH_SHADER_TYPE::FRAGMENT),
std::make_pair(COMPUTER_SHADER, SHADE::SH_SHADER_TYPE::COMPUTE) std::make_pair(COMPUTER_SHADER, SHADE::SH_SHADER_TYPE::COMPUTE)
}; };
constexpr size_t SHADER_TYPE_MAX_COUNT{ 3 }; constexpr size_t SHADER_TYPE_MAX_COUNT{ 3 };

View File

@ -646,31 +646,6 @@ namespace SHADE
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID); 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 else
{ {

View File

@ -22,6 +22,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Assets/Asset Types/SHTextureAsset.h" #include "Assets/Asset Types/SHTextureAsset.h"
#include "Assets/Asset Types/SHShaderAsset.h" #include "Assets/Asset Types/SHShaderAsset.h"
#include "Assets/Asset Types/SHAnimClipAsset.h" #include "Assets/Asset Types/SHAnimClipAsset.h"
#include "Assets/Asset Types/SHAnimControllerAsset.h"
#include "Graphics/Shaders/SHVkShaderModule.h" #include "Graphics/Shaders/SHVkShaderModule.h"
#include "Graphics/MiddleEnd/Textures/SHTextureLibrary.h" #include "Graphics/MiddleEnd/Textures/SHTextureLibrary.h"
#include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h" #include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h"
@ -40,6 +41,7 @@ namespace SHADE
class SHMaterial; class SHMaterial;
struct SHRigNode; struct SHRigNode;
class SHAnimationClip; class SHAnimationClip;
class SHAnimationController;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
@ -48,16 +50,17 @@ namespace SHADE
/// Template structs that maps a resource to their loaded asset representation type. /// Template structs that maps a resource to their loaded asset representation type.
/// </summary> /// </summary>
template<typename T = void> template<typename T = void>
struct SHResourceLoader { using AssetType = void; }; struct SHResourceLoader { using AssetType = void; };
template<> struct SHResourceLoader<SHMesh> { using AssetType = SHMeshAsset; }; template<> struct SHResourceLoader<SHMesh> { using AssetType = SHMeshAsset; };
template<> struct SHResourceLoader<SHTexture> { using AssetType = SHTextureAsset; }; template<> struct SHResourceLoader<SHTexture> { using AssetType = SHTextureAsset; };
template<> struct SHResourceLoader<SHVkShaderModule> { using AssetType = SHShaderAsset; }; template<> struct SHResourceLoader<SHVkShaderModule> { using AssetType = SHShaderAsset; };
template<> struct SHResourceLoader<SHMaterialSpec> { using AssetType = SHMaterialAsset; }; template<> struct SHResourceLoader<SHMaterialSpec> { using AssetType = SHMaterialAsset; };
template<> struct SHResourceLoader<SHMaterial> { using AssetType = SHMaterialSpec; }; template<> struct SHResourceLoader<SHMaterial> { using AssetType = SHMaterialSpec; };
template<> struct SHResourceLoader<SHFont> { using AssetType = SHFontAsset; }; template<> struct SHResourceLoader<SHFont> { using AssetType = SHFontAsset; };
template<> struct SHResourceLoader<SHRawAnimation> { using AssetType = SHModelAsset; }; template<> struct SHResourceLoader<SHRawAnimation> { using AssetType = SHModelAsset; };
template<> struct SHResourceLoader<SHRig> { using AssetType = SHModelAsset; }; template<> struct SHResourceLoader<SHRig> { using AssetType = SHModelAsset; };
template<> struct SHResourceLoader<SHAnimationClip> { using AssetType = SHAnimClipAsset; }; template<> struct SHResourceLoader<SHAnimationClip> { using AssetType = SHAnimClipAsset; };
template<> struct SHResourceLoader<SHAnimationController> { using AssetType = SHAnimControllerAsset; };
/// <summary> /// <summary>
/// Static class responsible for loading and caching runtime resources from their /// Static class responsible for loading and caching runtime resources from their

View File

@ -36,14 +36,16 @@ namespace SHADE
Handle<ResourceType> SHResourceManager::LoadOrGet(AssetID assetId) Handle<ResourceType> SHResourceManager::LoadOrGet(AssetID assetId)
{ {
// Check if it is an unsupported type // Check if it is an unsupported type
if (!std::is_same_v<ResourceType, SHMesh> && if (!std::is_same_v<ResourceType, SHMesh> &&
!std::is_same_v<ResourceType, SHTexture> && !std::is_same_v<ResourceType, SHTexture> &&
!std::is_same_v<ResourceType, SHVkShaderModule> && !std::is_same_v<ResourceType, SHVkShaderModule> &&
!std::is_same_v<ResourceType, SHMaterialSpec> && !std::is_same_v<ResourceType, SHMaterialSpec> &&
!std::is_same_v<ResourceType, SHFont> && !std::is_same_v<ResourceType, SHFont> &&
!std::is_same_v<ResourceType, SHMaterial> && !std::is_same_v<ResourceType, SHMaterial> &&
!std::is_same_v<ResourceType, SHRawAnimation> && !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."); static_assert(true, "Unsupported Resource Type specified for SHResourceManager.");
@ -361,7 +363,7 @@ namespace SHADE
loadedAssetData.emplace_back(assetId); loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType>(*assetData.anims[0]); 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); loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType> return resourceHub.Create<ResourceType>
@ -371,5 +373,13 @@ namespace SHADE
assetData->lastIndex assetData->lastIndex
); );
} }
else if constexpr (std::is_same_v<ResourceType, SHAnimationController>)
{
loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType>
(
// TODO
);
}
} }
} }

View File

@ -394,13 +394,13 @@ namespace YAML
struct convert<SHAnimatorComponent> struct convert<SHAnimatorComponent>
{ {
static constexpr std::string_view RIG_YAML_TAG = "Rig"; 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) static YAML::Node encode(SHAnimatorComponent const& rhs)
{ {
YAML::Node node; YAML::Node node;
node[RIG_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHRig>(rhs.GetRig()).value_or(0); 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; return node;
} }
static bool decode(YAML::Node const& node, SHAnimatorComponent& rhs) 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>())); 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; return true;
} }