Implemented Animation Clip asset and animation controller #410

Merged
XiaoQiDigipen merged 66 commits from SP3-22-AnimationController into main 2023-03-09 16:19:40 +08:00
61 changed files with 4391 additions and 439 deletions

View File

@ -0,0 +1,7 @@
Name: MD_RigTest01_SkinningTestAnims
ID: 203438081
Type: 12
Sub Assets:
Name: Full
ID: 231416496
Type: 13

BIN
Assets/Animation Clips/Main Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,40 @@
Name: racoonAnims
ID: 201804216
Type: 12
Sub Assets:
Name: TPose
ID: 231493784
Type: 13
Name: Idle
ID: 227450439
Type: 13
Name: Run
ID: 229125027
Type: 13
Name: Pickup
ID: 219605278
Type: 13
Name: Carry_Idle
ID: 231128260
Type: 13
Name: Carry_Run
ID: 227671720
Type: 13
Name: Throw
ID: 223399345
Type: 13
Name: Sprint
ID: 228149757
Type: 13
Name: Jump_Start
ID: 223009573
Type: 13
Name: Jump_Loop
ID: 230974023
Type: 13
Name: Jump_End
ID: 228134756
Type: 13
Name: Full
ID: 223752972
Type: 13

View File

@ -10,6 +10,7 @@
Color: {x: 1, y: 1, z: 1, w: 1} Color: {x: 1, y: 1, z: 1, w: 1}
Layer: 4294967295 Layer: 4294967295
Strength: 1 Strength: 1
Casting Shadows: false
IsActive: true IsActive: true
Scripts: ~ Scripts: ~
- EID: 2 - EID: 2
@ -18,16 +19,64 @@
NumberOfChildren: 0 NumberOfChildren: 0
Components: Components:
Transform Component: Transform Component:
Translate: {x: 0, y: 0, z: 0} Translate: {x: -0.291508496, y: 0, z: 0}
Rotate: {x: 0, y: 0, z: 0} Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 1, y: 1, z: 1} Scale: {x: 1, y: 1, z: 1}
IsActive: true IsActive: true
Renderable Component: Renderable Component:
Mesh: 148542784 Mesh: 149697411
Material: 121518381 Material: 128805346
IsActive: true IsActive: true
Animator Component: Animator Component:
Rig: 76586906 Rig: 77816045
Clip: 76586906 AnimationController: 0
IsActive: true
Scripts:
- Type: SHADE.Test.AnimTest
Enabled: true
fullClip: 223752972
idleClip: 227450439
runClip: 229125027
pickUpClip: 219605278
- EID: 1
Name: Default
IsActive: true
NumberOfChildren: 0
Components:
Camera Component:
Position: {x: 0, y: 0.5, z: 0.699999988}
Pitch: 0
Yaw: 0
Roll: 0
Width: 1920
Near: 0.00999999978
Far: 10000
Perspective: true
FOV: 90
IsActive: true IsActive: true
Scripts: ~ Scripts: ~
- EID: 3
Name: Leg
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0.332949668, y: 0, z: 0}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 0.0710000023, y: 0.0710000023, z: 0.0710000023}
IsActive: true
Renderable Component:
Mesh: 141097368
Material: 128805346
IsActive: true
Animator Component:
Rig: 72178939
AnimationController: 0
IsActive: true
Scripts:
- Type: SHADE.Test.AnimTest
Enabled: true
fullClip: 231416496
idleClip: 0
runClip: 0
pickUpClip: 0

View File

@ -0,0 +1,54 @@
using System;
namespace SHADE.Test
{
public class AnimTest : Script
{
#region Serialized Fields
[SerializeField]
private AnimationClipAsset fullClip;
[SerializeField]
private AnimationClipAsset idleClip;
[SerializeField]
private AnimationClipAsset runClip;
[SerializeField]
private AnimationClipAsset pickUpClip;
#endregion
#region Components
public Animator Animator { get; private set; }
#endregion
#region Lifecycle Functions
protected override void awake()
{
Animator = GetComponent<Animator>();
}
protected override void update()
{
// Play loop if shift is held
Action<AnimationClipAsset> playFunc = Input.GetKey(Input.KeyCode.LeftShift) ? (x) => Animator.Play(x)
: (x) => Animator.PlayOneShot(x);
// Play animations
if (Input.GetKeyUp(Input.KeyCode.Equals))
{
playFunc(fullClip);
}
else if (Input.GetKeyUp(Input.KeyCode.Alpha1))
{
playFunc(idleClip);
}
else if (Input.GetKeyUp(Input.KeyCode.Alpha2))
{
playFunc(runClip);
}
else if (Input.GetKeyUp(Input.KeyCode.Alpha3))
{
playFunc(pickUpClip);
}
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
Name: AnimTest
ID: 165676130
Type: 9

View File

@ -179,6 +179,9 @@ namespace Sandbox
// Link up SHDebugDraw // Link up SHDebugDraw
SHDebugDraw::Init(SHSystemManager::GetSystem<SHDebugDrawSystem>()); SHDebugDraw::Init(SHSystemManager::GetSystem<SHDebugDrawSystem>());
auto clip = SHResourceManager::LoadOrGet<SHRawAnimation>(77816045);
auto rig = SHResourceManager::LoadOrGet<SHRig>(77816045);
} }
void SBApplication::Update(void) void SBApplication::Update(void)

View File

@ -2,10 +2,10 @@
\file SHAnimationClip.cpp \file SHAnimationClip.cpp
\author Tng Kah Wei, kahwei.tng, 390009620 \author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu \par email: kahwei.tng\@digipen.edu
\date Nov 20, 2022 \date Feb 27, 2023
\brief Contains the function definitions of the SHAnimationClip class. \brief Contains the function definitions of the SHAnimationClip class.
Copyright (C) 2022 DigiPen Institute of Technology. Copyright (C) 2023 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited. of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/ *//*************************************************************************************/
@ -13,61 +13,26 @@ 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
{ {
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Constructors */ /* Constructors */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHAnimationClip::SHAnimationClip(const SHAnimAsset& asset) SHAnimationClip::SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame)
: rawAnim { rawAnimHandle }
, startFrameIndex { firstFrame }
, endFrameIndex { lastFrame }
, duration { 0.0f }
, startTimeStamp { 0.0f }
{ {
// Populate keyframes if (!rawAnim)
int maxFrames = 0; return;
totalTime = 0.0f;
for (const auto& channel : asset.nodeChannels)
{
// Create a channel
Channel newChannel;
newChannel.PositionKeyFrames.reserve(channel.positionKeys.size());
newChannel.RotationKeyFrames.reserve(channel.rotationKeys.size());
newChannel.ScaleKeyFrames.reserve(channel.scaleKeys.size());
// Populate Keyframes const float SECS_PER_TICK = 1.0f / static_cast<float>(rawAnim->GetTicksPerSecond());
for (const auto& posKey : channel.positionKeys) const int ONE_PAST_LAST_FRAME = lastFrame + 1;
{ duration = static_cast<float>(ONE_PAST_LAST_FRAME - firstFrame) * SECS_PER_TICK;
newChannel.PositionKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ posKey.time, posKey.value}); startTimeStamp = static_cast<float>(firstFrame) * SECS_PER_TICK;
} }
for (const auto& rotKey : channel.rotationKeys)
{
newChannel.RotationKeyFrames.emplace_back(SHAnimationKeyFrame<SHQuaternion>{ rotKey.time, rotKey.value});
}
for (const auto& scaleKey : channel.scaleKeys)
{
newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame<SHVec3>{ scaleKey.time, scaleKey.value });
}
newChannel.MaxFrames = std::max({ newChannel.PositionKeyFrames.size(), newChannel.RotationKeyFrames.size(), newChannel.ScaleKeyFrames.size() });
// Compute max frames
maxFrames = std::max(maxFrames, newChannel.MaxFrames);
// Compute total time
totalTime = std::max({ totalTime, newChannel.PositionKeyFrames.back().TimeStamp, newChannel.RotationKeyFrames.back().TimeStamp, newChannel.ScaleKeyFrames.back().TimeStamp });
// Insert the channel
channels.emplace_back(std::move(newChannel));
}
// Compute fps
ticksPerSecond = static_cast<int>(maxFrames / totalTime);
}
/*-----------------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
} }

View File

@ -2,10 +2,10 @@
\file SHAnimationClip.h \file SHAnimationClip.h
\author Tng Kah Wei, kahwei.tng, 390009620 \author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu \par email: kahwei.tng\@digipen.edu
\date Dec 12, 2022 \date Feb 27, 2023
\brief Contains the definition of the SHAnimationClip struct and related types. \brief Contains the definition of the SHAnimationClip struct and related types.
Copyright (C) 2022 DigiPen Institute of Technology. Copyright (C) 2023 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited. of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/ *//*************************************************************************************/
@ -14,71 +14,52 @@ of DigiPen Institute of Technology is prohibited.
// Project Includes // Project Includes
#include "SH_API.h" #include "SH_API.h"
#include "Math/SHMatrix.h" #include "Math/SHMatrix.h"
#include "Assets/Asset Types/Models/SHAnimationAsset.h" #include "Resource/SHHandle.h"
namespace SHADE namespace SHADE
{ {
/*-----------------------------------------------------------------------------------*/
/* Forward Declarations */
/*-----------------------------------------------------------------------------------*/
class SHRawAnimation;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/// <summary> /// <summary>
/// Defines a single key frame in an animation for a specific type of data. /// Represents a snippet of 3D animation that is stored in a SHRawAnimation object.
/// </summary>
template<typename T>
struct SHAnimationKeyFrame
{
float TimeStamp;
T Data;
};
/// <summary>
/// Represents a animation clip of a 3D animation that is made for a specific model
/// rig.
/// </summary> /// </summary>
class SH_API SHAnimationClip class SH_API SHAnimationClip
{ {
public: public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Defines the animations of a single bone in a rig.
/// </summary>
struct Channel
{
std::vector<SHAnimationKeyFrame<SHVec3>> PositionKeyFrames;
std::vector<SHAnimationKeyFrame<SHQuaternion>> RotationKeyFrames;
std::vector<SHAnimationKeyFrame<SHVec3>> ScaleKeyFrames;
int MaxFrames;
};
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Constructors */ /* Constructors */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/// <summary> /// <summary>
/// Constructs an SHAnimation Clip from a specified SHAnimAsset. /// Constructs an animation clip that contains the following parameters.
/// </summary> /// </summary>
/// <param name="asset">Animation asset to load.</param> /// <param name="rawAnimHandle">Handle to the raw animation data.</param>
explicit SHAnimationClip(const SHAnimAsset& asset); /// <param name="firstFrame">First frame to be played.</param>
/// <param name="lastFrame">Last frame to be played.</param>
SHAnimationClip(Handle<SHRawAnimation> rawAnimHandle, int firstFrame, int lastFrame);
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
const std::vector<Channel>& GetChannels() const noexcept { return channels; } inline Handle<SHRawAnimation> GetRawAnimation() const noexcept { return rawAnim; }
int GetTicksPerSecond() const noexcept { return ticksPerSecond; } inline int GetStartFrameIndex() const noexcept { return startFrameIndex; }
float GetTotalTime() const noexcept { return totalTime; } inline int GetEndFrameIndex() const noexcept { return endFrameIndex; }
inline float GetTotalDuration() const noexcept { return duration; }
inline float GetStartTimeStamp() const noexcept { return startTimeStamp; }
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
std::vector<Channel> channels; Handle<SHRawAnimation> rawAnim;
int ticksPerSecond; int startFrameIndex; // First Frame
float totalTime; int endFrameIndex; // Last Frame (inclusive)
float duration; // Total playback time
/*---------------------------------------------------------------------------------*/ float startTimeStamp; // Starting time stamp of the raw anim
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
}; };
} }

View File

@ -0,0 +1,228 @@
/************************************************************************************//*!
\file SHAnimationController.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Feb 22, 2023
\brief Contains the definition of SHAnimationController's functions.
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.
*//*************************************************************************************/
#include "SHpch.h"
#include "SHAnimationController.h"
#include "SHAnimationSystem.h"
#include "ECS_Base/Managers/SHSystemManager.h"
#include "SHAnimationClip.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* AnimParam Functions */
/*-----------------------------------------------------------------------------------*/
SHAnimationController::AnimParam::AnimParam(Type type)
: ParamType { type }
, Value { 0.0f }
{}
/*-----------------------------------------------------------------------------------*/
/* Transition - Usage Functions */
/*-----------------------------------------------------------------------------------*/
bool SHAnimationController::Transition::EvaluateCondition(const AnimParam& testParam) const noexcept
{
// Don't match, instant fail
if (testParam.ParamType != Param.ParamType)
return false;
// Evaluate them accordingly
switch (Param.ParamType)
{
case AnimParam::Type::Bool:
case AnimParam::Type::Trigger:
return evaluateCondition<bool>(testParam.Value != 0.0f);
case AnimParam::Type::Float:
return evaluateCondition<float>(testParam.Value);
break;
case AnimParam::Type::Int:
return evaluateCondition<int>(static_cast<int>(testParam.Value));
break;
}
return false;
}
/*-----------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*-----------------------------------------------------------------------------------*/
void SHAnimationController::Update(InstanceData& instData, float dt)
{
// Is there a valid node
if (!instData.CurrentNode)
return;
// Update the current playback
instData.ClipPlaybackTime += dt;
// Check if we finished playing
const float CLIP_CURR_PLAYED_TIME = instData.ClipPlaybackTime - instData.CurrentNode->Clip->GetStartTimeStamp();
if (CLIP_CURR_PLAYED_TIME > instData.CurrentNode->Clip->GetTotalDuration())
{
// Clamp
instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetStartTimeStamp() + instData.CurrentNode->Clip->GetTotalDuration();
// Go to next state
bool stateChanged = false;
for (const auto& transition : instData.CurrentNode->Transitions)
{
// Check for no condition special case
if (transition.Condition == Transition::ConditionType::None)
{
changeNode(instData, transition.Target);
stateChanged = true;
break;
}
else
{
// Check if we have the parameter
if (!instData.Params.contains(transition.ParamName))
continue;
// If evaluation success, we transition
AnimParam& param = instData.Params[transition.ParamName];
if (transition.EvaluateCondition(param))
{
changeNode(instData, transition.Target);
stateChanged = true;
// If trigger, we need to unset it
if (param.ParamType == AnimParam::Type::Trigger)
{
param.Value = false;
}
break;
}
}
}
// Handle if there is no next state, we repeat
if (!stateChanged)
{
instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetStartTimeStamp();
}
}
}
/*-----------------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------------*/
Handle<SHAnimationController::Node> SHAnimationController::CreateNode()
{
// Get system
auto system = SHSystemManager::GetSystem<SHAnimationSystem>();
if (system == nullptr)
throw std::runtime_error("[SHAnimationController] No SHAnimationSystem found!");
// Construct
auto node = system->GetResourceHub().Create<Node>();
nodes.emplace_back(node);
// If there is no start node, this is the first node so make it the starting node
if (!StartingNode)
StartingNode = node;
return node;
}
void SHAnimationController::DestroyNode(Handle<Node> node)
{
// Remove from storage
auto iter = std::find(nodes.begin(), nodes.end(), node);
if (iter == nodes.end())
throw std::invalid_argument("[SHAnimationController] Attempted to delete a node that doesn't belong.");
// Remove if it is a start node
if (StartingNode == node)
StartingNode = {};
// Remove from nodes
nodes.erase(iter);
// Clear node
node.Free();
}
void SHAnimationController::AddTransition(Handle<Node> source, const Transition& transition)
{
if (!source)
{
SHLOG_ERROR("[SHAnimationController] Attempted to add transition from an invalid node.");
return;
}
if (!transition.Target)
{
SHLOG_ERROR("[SHAnimationController] Attempted to add transition to an invalid node.");
return;
}
if (transition.Condition != Transition::ConditionType::None && !parameters.contains(transition.ParamName))
{
SHLOG_ERROR("[SHAnimationController] Attempted to add a conditional transition for an invalid parameter.");
return;
}
source->Transitions.emplace_back(transition);
}
void SHAnimationController::AddParameter(const std::string& name, AnimParam::Type type)
{
if (name.empty())
{
SHLOG_ERROR("[SHAnimationController] Attempted to add a parameter with no name.");
return;
}
if (parameters.contains(name))
{
SHLOG_ERROR("[SHAnimationController] Attempted to add a parameter with the same name.");
return;
}
// Insert
parameters.emplace(name, type);
}
void SHAnimationController::RemoveParameter(const std::string& name)
{
if (!parameters.contains(name))
{
SHLOG_ERROR("[SHAnimationController] Attempted to reemove a parameter that does not exist.");
return;
}
parameters.erase(name);
}
void SHAnimationController::SetTrigger(InstanceData& instData, const std::string& paramName)
{
// Invalid param
if (!parameters.contains(paramName))
return;
// Not a trigger
if (parameters[paramName] != AnimParam::Type::Trigger)
return;
// Set the flag
instData.Params[paramName].Value = true;
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
void SHAnimationController::changeNode(InstanceData& instData, Handle<Node> newNode)
{
instData.CurrentNode = newNode;
instData.ClipPlaybackTime = 0.0f;
}
}

View File

@ -0,0 +1,252 @@
/************************************************************************************//*!
\file SHAnimationController.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Feb 22, 2023
\brief Contains the definition of SHAnimationController.
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
// STL Includes
#include <variant>
#include <vector>
#include <optional>
// Project Includes
#include "SH_API.h"
#include "Resource/SHHandle.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Forward Declarations */
/*-----------------------------------------------------------------------------------*/
class SHAnimationClip;
/*-----------------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------------*/
/// <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
{
public:
/*---------------------------------------------------------------------------------*/
/* Forward Declarations */
/*---------------------------------------------------------------------------------*/
struct Node;
/*---------------------------------------------------------------------------------*/
/* Type Definition */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Describes a parameter for the AnimationController that can be used to control
/// the flow of animations.
/// </summary>
struct SH_API AnimParam
{
/*-------------------------------------------------------------------------------*/
/* Type Definition */
/*-------------------------------------------------------------------------------*/
/// <summary>
/// Type of animation parameter.
/// </summary>
enum class Type
{
Bool,
Trigger, // Variant of bool that can only be set to true and will be unset when consumed.
Float,
Int
};
using ValueType = float;
/*-------------------------------------------------------------------------------*/
/* 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 = Type::Int);
/*-------------------------------------------------------------------------------*/
/* Data Members */
/*-------------------------------------------------------------------------------*/
Type ParamType;
ValueType Value;
};
/// <summary>
/// Describes a transition between nodes of the animation controller.
/// </summary>
struct Transition
{
/*-------------------------------------------------------------------------------*/
/* Type Definition */
/*-------------------------------------------------------------------------------*/
/// <summary>
/// Types of conditions for the transition.
/// </summary>
enum class ConditionType
{
None,
Equals,
NotEquals,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual
};
/*-------------------------------------------------------------------------------*/
/* Data Members */
/*-------------------------------------------------------------------------------*/
Handle<Node> Target;
ConditionType Condition = ConditionType::None;
AnimParam Param;
std::string ParamName;
/*-------------------------------------------------------------------------------*/
/* Usage Functions */
/*-------------------------------------------------------------------------------*/
/// <summary>
/// Checks the condition of this Transition against an animation paramter.
/// </summary>
/// <param name="testParam">Parameter to test with.</param>
/// <returns>Whether the condition passed.</returns>
bool EvaluateCondition(const AnimParam& testParam) const noexcept;
private:
/*-------------------------------------------------------------------------------*/
/* Helper Functions */
/*-------------------------------------------------------------------------------*/
template<typename T>
bool evaluateCondition(T value) const noexcept;
};
/// <summary>
/// Describes a node in the animation controller.
/// </summary>
struct Node
{
std::string Name = "Unnamed Node";
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;
};
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
Handle<Node> StartingNode;
/*---------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Runs a single update for the animation controller.
/// </summary>
void Update(InstanceData& instData, float dt);
/*---------------------------------------------------------------------------------*/
/* Usage Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Creates a node in the state machine. Created nodes must be destroyed using
/// DestroyNode().
/// </summary>
/// <returns>Node that was created.</returns>
Handle<Node> CreateNode();
/// <summary>
/// Destroys the node that was created in the state machine.
/// </summary>
/// <param name="node">Node to destroy.</param>
void DestroyNode(Handle<Node> node);
/// <summary>
/// Links two nodes together with a Transition. This performs some additional
/// checking to ensure parameters are valid.
/// </summary>
/// <param name="source">Source node to transition from.</param>
/// <param name="transition">Describes the transition to add.</param>
void AddTransition(Handle<Node> source, const Transition& transition);
/// <summary>
/// Registers a parameter to the animation controller.
/// </summary>
/// <param name="name">Name of the parameter.</param>
/// <param name="type">Type of the parameter.</param>
void AddParameter(const std::string& name, AnimParam::Type type);
/// <summary>
/// Removes a parameter from the animation controller.
/// </summary>
/// <param name="name">Name of the parameter.</param>
void RemoveParameter(const std::string& name);
/// <summary>
/// Sets the parameter of the for the string. Does nothing if an invalid param name
/// is provided. Type of the parameter is not checked.
/// </summary>
/// <typeparam name="T">
/// Type of parameter. Only bool, int, floats are supported.
/// </typeparam>
/// <param name="instData">Data of the instance to update.</param>
/// <param name="paramName">Name of the parameter.</param>
/// <param name="value">Value to set the parameter to.</param>
template<typename T>
void SetParameter(InstanceData& instData, const std::string& paramName, T value);
/// <summary>
/// Gets the parameter of the for the string. Types are checked and will not return
/// a value if there is nothing.
/// </summary>
/// <typeparam name="T">
/// Type of parameter. Only bool, int, floats are supported.
/// </typeparam>
/// <param name="instData">Data of the instance to update.</param>
/// <param name="paramName">Name of the parameter.</param>
/// <returns>The value of the parameter or nothing if invalid.</returns>
template<typename T>
std::optional<T> GetParameter(InstanceData& instData, const std::string& paramName);
/// <summary>
/// Sets the flag for a trigger parameter. Does nothing if an invalid param name is
/// provided or if the param name refers to a parameter that is not a trigger.
/// </summary>
/// <param name="instData">Data of the instance to update.</param>
/// <param name="paramName">Name of the parameter.</param>
void SetTrigger(InstanceData& instData, const std::string& paramName);
/*---------------------------------------------------------------------------------*/
/* Getters */
/*---------------------------------------------------------------------------------*/
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
std::vector<Handle<Node>> nodes;
std::unordered_map<std::string, AnimParam::Type> parameters;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
void changeNode(InstanceData& instData, Handle<Node> newNode);
};
}
#include "SHAnimationController.hpp"

View File

@ -0,0 +1,121 @@
/************************************************************************************//*!
\file SHAnimationController.hpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 1, 2023
\brief Contains the definition of template functions SHAnimationController.
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 "SHAnimationController.h"
namespace SHADE
{
template<typename T>
bool SHAnimationController::Transition::evaluateCondition(T value) const noexcept
{
// Get the value
const T PARAM_VAL = [&]()
{
if constexpr (std::is_floating_point_v<T>)
return Param.Value;
else
return Param.Value != 0.0f;
}();
// Handle condition type
switch (Condition)
{
case SHAnimationController::Transition::ConditionType::None:
return true;
case SHAnimationController::Transition::ConditionType::Equals:
if constexpr (std::is_floating_point_v<T>)
{
static constexpr T EPSILON = static_cast<T>(0.001);
return std::abs(std::abs(value) - std::abs(PARAM_VAL)) < EPSILON;
}
else
{
return value == PARAM_VAL;
}
break;
case SHAnimationController::Transition::ConditionType::NotEquals:
if constexpr (std::is_floating_point_v<T>)
{
static constexpr T EPSILON = static_cast<T>(0.001);
return std::abs(std::abs(value) - std::abs(PARAM_VAL)) > EPSILON;
}
else
{
return value != PARAM_VAL;
}
break;
case SHAnimationController::Transition::ConditionType::LessThan:
return PARAM_VAL < value;
case SHAnimationController::Transition::ConditionType::LessThanOrEqual:
return PARAM_VAL <= value;
case SHAnimationController::Transition::ConditionType::GreaterThan:
return PARAM_VAL > value;
case SHAnimationController::Transition::ConditionType::GreaterThanOrEqual:
return PARAM_VAL >= value;
}
// Neither of the existing cases
return false;
}
template<typename T>
void SHAnimationController::SetParameter(InstanceData& instData, const std::string& paramName, T value)
{
static_assert(std::is_same_v<T, bool> || std::is_same_v<T, float> || std::is_same_v<T, int>, "Only works with bool, float or ints.");
// Invalid param
if (parameters.find(paramName) == parameters.end())
return;
// Set the value
instData.Params[paramName].Value = value;
}
template<typename T>
std::optional<T> SHAnimationController::GetParameter(InstanceData& instData, const std::string& paramName)
{
static_assert(std::is_same_v<T, bool> || std::is_same_v<T, float> || std::is_same_v<T, int>, "Only works with bool, float or ints.");
// Invalid param
if (parameters.find(paramName) == parameters.end())
return {};
// Check if the type matches
const auto TYPE = parameters[paramName];
if constexpr (std::is_same_v<T, bool>)
{
if (TYPE != AnimParam::Type::Bool && TYPE != AnimParam::Type::Trigger)
return {};
}
else if constexpr (std::is_same_v<T, float>)
{
if (parameters[paramName] != AnimParam::Type::Float)
return {};
}
else if constexpr (std::is_same_v<T, int>)
{
if (parameters[paramName] != AnimParam::Type::Int)
return {};
}
// Return the correct value
auto paramIter = instData.Params.find(paramName);
if (paramIter != instData.Params.end())
{
return paramIter->second.Value;
}
else
{
return T(); // Default constructed value
}
}
}

View File

@ -15,6 +15,7 @@ of DigiPen Institute of Technology is prohibited.
#include "SH_API.h" #include "SH_API.h"
#include "ECS_Base/System/SHSystem.h" #include "ECS_Base/System/SHSystem.h"
#include "ECS_Base/System/SHSystemRoutine.h" #include "ECS_Base/System/SHSystemRoutine.h"
#include "Resource/SHResourceLibrary.h"
namespace SHADE namespace SHADE
{ {
@ -51,5 +52,16 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
virtual void Init(void) override final; virtual void Init(void) override final;
virtual void Exit(void) override final; virtual void Exit(void) override final;
/*---------------------------------------------------------------------------------*/
/* Getters */
/*---------------------------------------------------------------------------------*/
SHResourceHub& GetResourceHub() { return resources; }
private:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
SHResourceHub resources;
}; };
} }

View File

@ -33,18 +33,62 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHAnimatorComponent::Play() void SHAnimatorComponent::Play()
{ {
isPlaying = false; isPlaying = true;
playOnce = false;
} }
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;
auto RAW_ANIM = clip->GetRawAnimation();
// Set up if valid
if (currClip && RAW_ANIM)
{
// Calculate secs for the clip
secsPerTick = 1.0f / RAW_ANIM->GetTicksPerSecond();
currPlaybackTime = currClip->GetStartTimeStamp();
// Start playback
Play(); Play();
// Set to initial pose
if (rig && rig->GetRootNode())
{
updateCurrentAnimatorState(currClip, 0.0f);
}
}
}
void SHAnimatorComponent::PlayOneShot(Handle<SHAnimationClip> clip)
{
Play(clip);
playOnce = true;
} }
void SHAnimatorComponent::PlayFromStart() void SHAnimatorComponent::PlayFromStart()
{ {
if (!currClip)
{
SHLOG_WARNING("[SHAnimatorComponent] Attempted to restart a clip but there is no existing clip. Ignored.");
return;
}
isPlaying = true; isPlaying = true;
currPlaybackTime = 0.0f; currPlaybackTime = 0.0f;
} }
@ -56,10 +100,47 @@ namespace SHADE
void SHAnimatorComponent::Stop() void SHAnimatorComponent::Stop()
{ {
if (!currClip)
{
SHLOG_WARNING("[SHAnimatorComponent] Attempted to stop a clip but there is no existing clip. Ignored.");
return;
}
isPlaying = false; isPlaying = false;
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 */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -79,60 +160,118 @@ 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;
// Set parameters // Set the controller
currClip = newClip; animController = ac;
secsPerTick = 1.0f / currClip->GetTicksPerSecond();
// Set to initial pose // If valid, we want to initialize it
if (rig && rig->GetRootNode() && currClip) if (animController)
{ {
updatePoseWithClip(0.0f); // Parameters
animInstanceData.Params.clear();
for (auto param : animController->GetParams())
{
animInstanceData.Params.emplace(param.first, SHAnimationController::AnimParam(param.second));
}
// First Node
animInstanceData.CurrentNode = animController->StartingNode;
// 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);
}
} }
} }
/*-----------------------------------------------------------------------------------*/ void SHAnimatorComponent::SetTrigger(const std::string& paramName)
/* Update Functions */
/*-----------------------------------------------------------------------------------*/
void SHAnimatorComponent::Update(float dt)
{ {
//Reset matrices if (!animController)
std::fill(boneMatrices.begin(), boneMatrices.end(), SHMatrix::Identity);
// Nothing to animate
if (!currClip || !isPlaying || !rig || !rig->GetRootNode())
return; return;
// Update time on the playback return animController->SetTrigger(animInstanceData, paramName);
}
/*-----------------------------------------------------------------------------------*/
/* 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();
}
}
void SHAnimatorComponent::updateManualClipState(float dt)
{
currPlaybackTime += dt; currPlaybackTime += dt;
if (currPlaybackTime > currClip->GetTotalTime()) const float CLIP_CURR_PLAYED_TIME = currPlaybackTime - currClip->GetStartTimeStamp();
if (CLIP_CURR_PLAYED_TIME > currClip->GetTotalDuration())
{ {
currPlaybackTime = currPlaybackTime - currClip->GetTotalTime(); if (playOnce)
{
playOnce = false;
isPlaying = false;
currPlaybackTime = currClip->GetStartTimeStamp() + currClip->GetTotalDuration();
} }
else
{
currPlaybackTime = currClip->GetStartTimeStamp();
}
}
}
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, playbackTime);
} }
void SHAnimatorComponent::updatePoseWithClip(Handle<SHAnimationClip> clip, float poseTime)
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
void SHAnimatorComponent::updatePoseWithClip(float poseTime)
{ {
updatePoseWithClip(poseTime, rig->GetRootNode(), SHMatrix::Identity); updatePoseWithClip(poseTime, clip->GetRawAnimation(), rig->GetRootNode(), SHMatrix::Identity);
} }
void SHAnimatorComponent::updatePoseWithClip(float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix) void SHAnimatorComponent::updatePoseWithClip(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
SHMatrix transformMatrix = node->TransformMatrix; SHMatrix transformMatrix = node->TransformMatrix;
const int BONE_IDX = rig->GetNodeIndex(node); const int BONE_IDX = rig->GetNodeIndex(node);
const auto& CHANNELS = currClip->GetChannels(); const auto& CHANNELS = rawAnimData->GetChannels();
if (BONE_IDX < CHANNELS.size()) if (BONE_IDX < CHANNELS.size())
{ {
const auto& CHANNEL = CHANNELS[BONE_IDX]; const auto& CHANNEL = CHANNELS[BONE_IDX];
@ -149,16 +288,15 @@ namespace SHADE
// Apply transformations to this node // Apply transformations to this node
const int BONE_MTX_IDX = rig->GetNodeIndex(node); const int BONE_MTX_IDX = rig->GetNodeIndex(node);
std::optional<SHVec3> position;
if (BONE_MTX_IDX >= 0) if (BONE_MTX_IDX >= 0)
{ {
boneMatrices[BONE_MTX_IDX] = node->OffsetMatrix * transformMatrix; boneMatrices[BONE_MTX_IDX] = node->OffsetMatrix * transformMatrix;
} }
// Apply pose to children // Apply pose to children
for (auto& child : node->Children) for (auto child : node->Children)
{ {
updatePoseWithClip(poseTime, child, transformMatrix); updatePoseWithClip(poseTime, rawAnimData, child, transformMatrix);
} }
} }
} }

View File

@ -22,7 +22,8 @@ of DigiPen Institute of Technology is prohibited.
#include "Math/SHMatrix.h" #include "Math/SHMatrix.h"
#include "Math/Vector/SHVec3.h" #include "Math/Vector/SHVec3.h"
#include "Math/SHQuaternion.h" #include "Math/SHQuaternion.h"
#include "SHAnimationClip.h" #include "SHRawAnimation.h"
#include "SHAnimationController.h"
namespace SHADE namespace SHADE
{ {
@ -32,7 +33,6 @@ namespace SHADE
class SHRig; class SHRig;
struct SHRigNode; struct SHRigNode;
class SHAnimationClip; class SHAnimationClip;
class SHVkBuffer;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
@ -52,12 +52,20 @@ 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"></param> /// <param name="clip">Animation clip to play.</param>
void Play(Handle<SHAnimationClip> clip); void Play(Handle<SHAnimationClip> clip);
/// <summary> /// <summary>
/// Plays the currently loaded animation clip from the start. /// 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);
/// <summary>
/// Plays the currently loaded animation clip from the start. Note that this only
/// works when using manual playback mode.
/// </summary> /// </summary>
void PlayFromStart(); void PlayFromStart();
/// <summary> /// <summary>
@ -65,10 +73,22 @@ namespace SHADE
/// </summary> /// </summary>
void Pause(); void Pause();
/// <summary> /// <summary>
/// Stops the animation and resets the play time back to 0. /// Stops the animation and resets the play time back to 0. Note that this only
/// works when using manual playback mode. This is not supported when using an
/// Animation Controller.
/// </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 */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -78,12 +98,44 @@ namespace SHADE
/// <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> /// <summary>
/// Sets the animation clip of this animator without playing it. /// Sets the animation controller to use for this animator.
/// 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> /// </summary>
/// <param name="newClip">Clip to use.</param> /// <param name="newRig">Animation controller to use.</param>
void SetClip(Handle<SHAnimationClip> newClip); void SetAnimationController(Handle<SHAnimationController> ac);
/*---------------------------------------------------------------------------------*/
/* Parameter Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Sets the parameter of the for the string. Does nothing if an invalid param name
/// is provided. Type of the parameter is not checked. Also does nothing if no
/// animation controller is specified.
/// </summary>
/// <typeparam name="T">
/// Type of parameter. Only bool, int, floats are supported.
/// </typeparam>
/// <param name="paramName">Name of the parameter.</param>
/// <param name="value">Value to set the parameter to.</param>
template<typename T>
void SetParameter(const std::string& paramName, T value);
/// <summary>
/// Gets the parameter of the for the string. Types are checked and will not return
/// a value if there is nothing. Returns nothing if there is no animation controller
/// specified either.
/// </summary>
/// <typeparam name="T">
/// Type of parameter. Only bool, int, floats are supported.
/// </typeparam>
/// <param name="paramName">Name of the parameter.</param>
/// <returns>The value of the parameter or nothing if invalid.</returns>
template<typename T>
std::optional<T> GetParameter(const std::string& paramName);
/// <summary>
/// Sets the flag for a trigger parameter. Does nothing if an invalid param name is
/// provided or if the param name refers to a parameter that is not a trigger.
/// </summary>
/// <param name="paramName">Name of the parameter.</param>
void SetTrigger(const std::string& paramName);
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
@ -99,38 +151,42 @@ 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; }
/// <summary>
/// Retrieves the currently set animation controller.
/// </summary>
/// <returnsHandle to the currently set animtion controller.</returns>
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 playOnce = false;
// Shared Tracking
bool isPlaying = true; bool isPlaying = true;
// Useful Cached Data
float secsPerTick = 0.0f; float secsPerTick = 0.0f;
// Buffer // Buffer
std::vector<SHMatrix> boneMatrices; std::vector<SHMatrix> boneMatrices;
@ -138,8 +194,11 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
void updatePoseWithClip(float poseTime); void updateAnimController(float dt);
void updatePoseWithClip(float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix); void updateManualClipState(float dt);
void updateCurrentAnimatorState(Handle<SHAnimationClip> clip, float playbackTime);
void updatePoseWithClip(Handle<SHAnimationClip> clip, float poseTime);
void updatePoseWithClip(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, float poseTime); T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, float poseTime);

View File

@ -15,13 +15,31 @@ of DigiPen Institute of Technology is prohibited.
// Project Includes // Project Includes
#include "SHRig.h" #include "SHRig.h"
#include "Math/SHMatrix.h" #include "Math/SHMatrix.h"
#include "SHAnimationClip.h" #include "SHRawAnimation.h"
#include "Graphics/SHVkUtil.h" #include "SHAnimationController.h"
#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h"
#include "ECS_Base/Managers/SHSystemManager.h"
namespace SHADE namespace SHADE
{ {
/*-----------------------------------------------------------------------------------*/
/* Setter Functions */
/*-----------------------------------------------------------------------------------*/
template<typename T>
std::optional<T> SHAnimatorComponent::GetParameter(const std::string& paramName)
{
if (!animController)
return {};
return animController->GetParameter<T>(animInstanceData, paramName);
}
template<typename T>
void SHAnimatorComponent::SetParameter(const std::string& paramName, T value)
{
if (!animController)
return;
return animController->SetParameter(animInstanceData, paramName, value);
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/

View File

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

View File

@ -0,0 +1,85 @@
/************************************************************************************//*!
\file SHRawAnimation.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 12, 2022
\brief Contains the definition of the SHRawAnimation struct and related types.
Copyright (C) 2022 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
// Project Includes
#include "SH_API.h"
#include "Math/SHMatrix.h"
#include "Assets/Asset Types/Models/SHAnimationAsset.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------------*/
/// <summary>
/// Defines a single key frame in an animation for a specific type of data.
/// </summary>
template<typename T>
struct SHAnimationKeyFrame
{
float TimeStamp;
T Data;
};
/// <summary>
/// Represents the raw 3D animation data for a rigged 3D model.
/// </summary>
class SH_API SHRawAnimation
{
public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Defines the animations of a single bone in a rig.
/// </summary>
struct Channel
{
std::vector<SHAnimationKeyFrame<SHVec3>> PositionKeyFrames;
std::vector<SHAnimationKeyFrame<SHQuaternion>> RotationKeyFrames;
std::vector<SHAnimationKeyFrame<SHVec3>> ScaleKeyFrames;
int MaxFrames;
};
/*---------------------------------------------------------------------------------*/
/* Constructors */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Constructs an SHAnimation Clip from a specified SHAnimAsset.
/// </summary>
/// <param name="asset">Animation asset to load.</param>
explicit SHRawAnimation(const SHAnimAsset& asset);
/*---------------------------------------------------------------------------------*/
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
const std::vector<Channel>& GetChannels() const noexcept { return channels; }
int GetTicksPerSecond() const noexcept { return ticksPerSecond; }
float GetTotalTime() const noexcept { return totalTime; }
int GetTotalFrames() const noexcept { return totalFrames; }
private:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
std::vector<Channel> channels;
int ticksPerSecond;
float totalTime;
int totalFrames;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
};
}

View File

@ -10,8 +10,10 @@
*****************************************************************************/ *****************************************************************************/
#pragma once #pragma once
#include "Math/SHMath.h"
#include "Assets/Asset Types/SHAssetData.h" #include "Assets/Asset Types/SHAssetData.h"
#include "Math/Vector/SHVec3.h"
#include "Math/Vector/SHVec4.h"
#include "Math/SHQuaternion.h"
#include <vector> #include <vector>

View File

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

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

@ -0,0 +1,128 @@
#include "SHpch.h"
#include "SHBinaryLoader.h"
#include "Assets/Asset Types/SHAnimClipContainerAsset.h"
#include <fstream>
namespace SHADE
{
SHAssetData* SHBinaryLoader::Load(AssetPath path)
{
std::ifstream file{ path, std::ios::in | std::ios::binary };
if (!file.is_open())
{
SHLOG_ERROR("[Binary Loader] Unable to open file for reading: {}", path.string());
return nullptr;
}
auto const extension = path.extension().string();
SHAssetData* result{nullptr};
if (extension == ANIM_CONTAINER_EXTENSION)
{
LoadAnimClipContainer(file, result, path);
}
file.close();
return result;
}
void SHBinaryLoader::Write(SHAssetData const* data, AssetPath path)
{
std::ofstream file{ path, std::ios::out | std::ios::binary };
if (!file.is_open())
{
SHLOG_ERROR("[Binary Loader] Unable to open file for writing: {}", path.string());
return;
}
auto const extension = path.extension().string();
if (extension == ANIM_CONTAINER_EXTENSION)
{
WriteAnimClipContainer(file, data, path);
}
file.close();
}
void SHBinaryLoader::WriteAnimClipContainer(std::ofstream& file, SHAssetData const* data, AssetPath path)
{
auto const& anim = *dynamic_cast<SHAnimClipContainerAsset const*>(data);
file.write(
reinterpret_cast<char const*>(&anim.animRawDataAssetId),
sizeof(uint32_t)
);
uint32_t const size {static_cast<uint32_t>(anim.clips.size())};
file.write(
reinterpret_cast<char const*>(&size),
sizeof(uint32_t)
);
for (auto const& clip : anim.clips)
{
uint32_t charCount {static_cast<uint32_t>(clip.name.size())};
file.write(
reinterpret_cast<char const*>(&charCount),
sizeof(uint32_t)
);
file.write(
clip.name.data(),
charCount
);
file.write(
reinterpret_cast<char const*>(&clip.firstIndex),
sizeof(uint32_t) * 2
);
}
}
void SHBinaryLoader::LoadAnimClipContainer(std::ifstream& file, SHAssetData*& result, AssetPath path)
{
auto const data = new SHAnimClipContainerAsset();
file.read(
reinterpret_cast<char*>(&data->animRawDataAssetId),
sizeof(uint32_t)
);
uint32_t size;
file.read(
reinterpret_cast<char*>(&size),
sizeof(uint32_t)
);
for (auto i{0}; i < size; ++i)
{
auto& clip {data->clips.emplace_back()};
uint32_t charCount;
file.read(
reinterpret_cast<char*>(&charCount),
sizeof(uint32_t)
);
clip.name.resize(charCount);
file.read(
clip.name.data(),
charCount
);
file.read(
reinterpret_cast<char*>(&clip.firstIndex),
sizeof(uint32_t) * 2
);
clip.animRawDataAssetId = data->animRawDataAssetId;
}
result = data;
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "SHAssetLoader.h"
namespace SHADE
{
struct SHBinaryLoader : SHAssetLoader
{
SHAssetData* Load(AssetPath path) override;
void Write(SHAssetData const* data, AssetPath path) override;
private:
//Individual functions to write files
void WriteAnimClipContainer(std::ofstream& file,SHAssetData const* data, AssetPath path);
void LoadAnimClipContainer(std::ifstream& file,SHAssetData*& result, AssetPath path);
};
}

View File

@ -35,32 +35,32 @@ namespace SHADE
if (!file.is_open()) if (!file.is_open())
{ {
SHLOG_ERROR("[Text Loader] Unable to open text File: {}", path.string()); SHLOG_ERROR("[Text Loader] Unable to open text file for reading: {}", path.string());
return nullptr; return nullptr;
} }
std::stringstream stream; std::stringstream stream;
stream << file.rdbuf(); stream << file.rdbuf();
std::string content = stream.str(); std::string content = stream.str();
auto const extension = path.extension().string();
SHAssetData* result; SHAssetData* result;
if (path.extension().string() == SCENE_EXTENSION) if (extension == SCENE_EXTENSION)
{ {
auto data = new SHSceneAsset(); auto data = new SHSceneAsset();
data->name = path.stem().string(); data->name = path.stem().string();
data->data = std::move(content); data->data = std::move(content);
result = data; result = data;
} }
else if (path.extension().string() == PREFAB_EXTENSION) else if (extension == PREFAB_EXTENSION)
{ {
auto data = new SHPrefabAsset(); auto data = new SHPrefabAsset();
data->name = path.stem().string(); data->name = path.stem().string();
data->data = std::move(content); data->data = std::move(content);
result = data; result = data;
} }
else if (path.extension().string() == MATERIAL_EXTENSION) else if (extension == MATERIAL_EXTENSION)
{ {
auto data = new SHMaterialAsset(); auto data = new SHMaterialAsset();
data->name = path.stem().string(); data->name = path.stem().string();
@ -79,21 +79,23 @@ namespace SHADE
if (!file.is_open()) if (!file.is_open())
{ {
SHLOG_ERROR("[Text Loader] Unable to open text File: {}", path.string()); SHLOG_ERROR("[Text Loader] Unable to open text file for writing: {}", path.string());
return; return;
} }
if (path.extension().string() == SCENE_EXTENSION) auto const extension = path.extension().string();
if (extension == SCENE_EXTENSION)
{ {
auto scene = dynamic_cast<SHSceneAsset const*>(data); auto scene = dynamic_cast<SHSceneAsset const*>(data);
file << scene->data; file << scene->data;
} }
else if (path.extension().string() == PREFAB_EXTENSION) else if (extension == PREFAB_EXTENSION)
{ {
auto prefab = dynamic_cast<SHPrefabAsset const*>(data); auto prefab = dynamic_cast<SHPrefabAsset const*>(data);
file << prefab->data; file << prefab->data;
} }
else if (path.extension().string() == MATERIAL_EXTENSION) else if (extension == MATERIAL_EXTENSION)
{ {
auto material = dynamic_cast<SHMaterialAsset const*>(data); auto material = dynamic_cast<SHMaterialAsset const*>(data);
file << material->data; file << material->data;

View File

@ -11,10 +11,6 @@
#pragma once #pragma once
#include "SHAssetLoader.h" #include "SHAssetLoader.h"
#include "Assets/Asset Types/SHPrefabAsset.h"
#include "Assets/Asset Types/SHSceneAsset.h"
#include "Assets/Asset Types/SHMaterialAsset.h"
namespace SHADE namespace SHADE
{ {
struct SHTextBasedLoader : SHAssetLoader struct SHTextBasedLoader : SHAssetLoader

View File

@ -7,6 +7,7 @@
* or disclosure of this file or its contents without the prior * or disclosure of this file or its contents without the prior
* written consent of Digipen Institute of Technology is prohibited * written consent of Digipen Institute of Technology is prohibited
******************************************************************************/ ******************************************************************************/
// ReSharper disable All
#ifndef SH_ASSET_MACROS_H #ifndef SH_ASSET_MACROS_H
#define SH_ASSET_MACROS_H #define SH_ASSET_MACROS_H
@ -28,7 +29,7 @@ enum FMOD_SPEAKERMODE : int;
// Typedefs // Typedefs
typedef uint32_t AssetID; typedef uint32_t AssetID;
typedef std::string AssetName; using AssetName = std::string;
typedef std::filesystem::path AssetPath; typedef std::filesystem::path AssetPath;
typedef unsigned char* AssetData; typedef unsigned char* AssetData;
typedef std::string AssetMetaVersion; typedef std::string AssetMetaVersion;
@ -57,6 +58,9 @@ enum class AssetType : AssetTypeMeta
SCRIPT, SCRIPT,
FONT, FONT,
AUDIO_BANK, AUDIO_BANK,
ANIM_CONTAINER,
ANIM_CLIP,
ANIM_CONTROLLER,
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) };
@ -78,6 +82,8 @@ constexpr std::string_view FONT_COMPILER_EXE{ "FontCompiler.exe" };
constexpr std::string_view SCENE_FOLDER{ "/Scenes/" }; constexpr std::string_view SCENE_FOLDER{ "/Scenes/" };
constexpr std::string_view PREFAB_FOLDER{ "/Prefabs/" }; constexpr std::string_view PREFAB_FOLDER{ "/Prefabs/" };
constexpr std::string_view MATERIAL_FOLDER{ "/Materials/" }; constexpr std::string_view MATERIAL_FOLDER{ "/Materials/" };
constexpr std::string_view ANIM_CLIP_FOLDER{ "/Animation Clips/" };
constexpr std::string_view ANIM_CONTROLLER_FOLDER{ "/Animation Controllers/" };
// ASSET EXTENSIONS // ASSET EXTENSIONS
@ -94,6 +100,9 @@ constexpr std::string_view PREFAB_EXTENSION {".shprefab"};
constexpr std::string_view MATERIAL_EXTENSION {".shmat"}; constexpr std::string_view MATERIAL_EXTENSION {".shmat"};
constexpr std::string_view TEXTURE_EXTENSION {".shtex"}; 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 ANIM_CONTAINER_EXTENSION{ ".shanimcontainer" };
constexpr std::string_view ANIM_CONTROLLER_EXTENSION{ ".shanimcontroller" };
constexpr std::string_view FILLER_EXTENSION{"dummy"};
constexpr std::string_view EXTENSIONS[] = { constexpr std::string_view EXTENSIONS[] = {
AUDIO_EXTENSION, AUDIO_EXTENSION,
@ -104,10 +113,13 @@ constexpr std::string_view EXTENSIONS[] = {
SCENE_EXTENSION, SCENE_EXTENSION,
PREFAB_EXTENSION, PREFAB_EXTENSION,
MATERIAL_EXTENSION, MATERIAL_EXTENSION,
"dummy", FILLER_EXTENSION,
SCRIPT_EXTENSION, SCRIPT_EXTENSION,
FONT_EXTENSION, FONT_EXTENSION,
AUDIO_BANK_EXTENSION AUDIO_BANK_EXTENSION,
ANIM_CONTAINER_EXTENSION,
ANIM_CONTROLLER_EXTENSION,
FILLER_EXTENSION
}; };
constexpr size_t EXTENSIONS_COUNT{ static_cast<size_t>(AssetType::MAX_COUNT) }; constexpr size_t EXTENSIONS_COUNT{ static_cast<size_t>(AssetType::MAX_COUNT) };

View File

@ -21,8 +21,13 @@
#include "Libraries/Loaders/SHShaderSourceLoader.h" #include "Libraries/Loaders/SHShaderSourceLoader.h"
#include "Libraries/Loaders/SHTextBasedLoader.h" #include "Libraries/Loaders/SHTextBasedLoader.h"
#include "Libraries/Loaders/SHFontLoader.h" #include "Libraries/Loaders/SHFontLoader.h"
#include "Libraries/Loaders/SHBinaryLoader.h"
#include "Asset Types/SHPrefabAsset.h"
#include "Asset Types/SHMaterialAsset.h"
#include "Asset Types/SHSceneAsset.h"
#include "Asset Types/SHAnimClipContainerAsset.h"
//#include "Libraries/Compilers/SHMeshCompiler.h"
#include "Libraries/Compilers/SHTextureCompiler.h" #include "Libraries/Compilers/SHTextureCompiler.h"
#include "Libraries/Compilers/SHShaderSourceCompiler.h" #include "Libraries/Compilers/SHShaderSourceCompiler.h"
@ -159,14 +164,24 @@ namespace SHADE
return AssetType::INVALID; return AssetType::INVALID;
} }
std::optional<SHADE::SHAsset> SHAssetManager::GetAsset(AssetID id) noexcept SHAsset* SHAssetManager::GetAsset(AssetID id) noexcept
{ {
if (assetCollection.contains(id)) if (assetCollection.contains(id))
{ {
return assetCollection[id]; return &assetCollection[id];
} }
return {}; return nullptr;
}
SHAsset const* SHAssetManager::GetAssetConst(AssetID id) noexcept
{
if (assetCollection.contains(id))
{
return &assetCollection[id];
}
return nullptr;
} }
AssetID SHAssetManager::GetAssetIDFromPath(AssetPath const& path) noexcept AssetID SHAssetManager::GetAssetIDFromPath(AssetPath const& path) noexcept
@ -233,8 +248,19 @@ namespace SHADE
} }
break; break;
case AssetType::ANIM_CONTAINER:
newPath += ANIM_CLIP_FOLDER;
newPath += name;
newPath += ANIM_CONTAINER_EXTENSION;
{
auto animClip = new SHAnimClipContainerAsset();
data = animClip;
}
break;
default: default:
SHLOG_ERROR("[Asset Manager] Asset type of {} not an internal asset type, cannot be created", name); SHLOG_ERROR("[Asset Manager] Asset type of {} not an internal parent asset type, cannot be created", name);
return 0; return 0;
} }
@ -266,6 +292,41 @@ namespace SHADE
return id; return id;
} }
AssetID SHAssetManager::CreateNewSubAsset(AssetType type, AssetName name, AssetID parent)
{
if (!assetData.contains(parent))
{
SHLOG_ERROR("[Asset Manager] Failed to create new sub asset, parent does not exist: {}", name);
return 0;
}
switch(type)
{
case AssetType::ANIM_CLIP:
{
auto const animContainer {dynamic_cast<SHAnimClipContainerAsset*>(assetData[parent])};
auto id = GenerateAssetID(type);
SHAsset asset{
.name = name,
.id = id,
.type = type,
.isSubAsset = true,
.parent = parent
};
auto& newClip {animContainer->clips.emplace_back()};
newClip.name = name;
assetCollection.emplace(id, asset);
assetCollection[parent].subAssets.push_back(&assetCollection[id]);
assetData.emplace(id, &newClip);
return id;
}
default:
SHLOG_ERROR("[Asset Manager] Asset type of {} not an internal sub asset type, cannot be created", name);
return 0;
}
}
bool SHAssetManager::SaveAsset(AssetID id) noexcept bool SHAssetManager::SaveAsset(AssetID id) noexcept
{ {
if (assetCollection.contains(id)) if (assetCollection.contains(id))
@ -274,7 +335,8 @@ namespace SHADE
if ( if (
asset.type == AssetType::SCENE || asset.type == AssetType::SCENE ||
asset.type == AssetType::PREFAB || asset.type == AssetType::PREFAB ||
asset.type == AssetType::MATERIAL asset.type == AssetType::MATERIAL ||
asset.type == AssetType::ANIM_CONTAINER
) )
{ {
if (assetData.contains(id)) if (assetData.contains(id))
@ -467,9 +529,9 @@ namespace SHADE
//Reload data //Reload data
auto result = GetAsset(target); auto result = GetAsset(target);
if (result.has_value()) if (result)
{ {
auto const& asset{ result.value() }; auto const& asset{ *result };
auto newData = loaders[static_cast<size_t>(asset.type)]->Load(asset.path); auto newData = loaders[static_cast<size_t>(asset.type)]->Load(asset.path);
delete assetData[target]; delete assetData[target];
assetData[target] = newData; assetData[target] = newData;
@ -533,6 +595,8 @@ namespace SHADE
loaders[static_cast<size_t>(AssetType::SCRIPT)] = nullptr; loaders[static_cast<size_t>(AssetType::SCRIPT)] = nullptr;
loaders[static_cast<size_t>(AssetType::FONT)] = dynamic_cast<SHAssetLoader*>(new SHFontLoader()); loaders[static_cast<size_t>(AssetType::FONT)] = dynamic_cast<SHAssetLoader*>(new SHFontLoader());
loaders[static_cast<size_t>(AssetType::AUDIO_BANK)] = loaders[static_cast<size_t>(AssetType::SCENE)]; loaders[static_cast<size_t>(AssetType::AUDIO_BANK)] = loaders[static_cast<size_t>(AssetType::SCENE)];
loaders[static_cast<size_t>(AssetType::ANIM_CONTAINER)] = dynamic_cast<SHAssetLoader*>(new SHBinaryLoader());
loaders[static_cast<size_t>(AssetType::ANIM_CLIP)] = nullptr;
} }
/**************************************************************************** /****************************************************************************
@ -590,9 +654,11 @@ namespace SHADE
else else
{ {
assetData.emplace(parent.id, parentData); assetData.emplace(parent.id, parentData);
if (parent.type == AssetType::MODEL) switch(parent.type)
{ {
auto parentModel = reinterpret_cast<SHModelAsset*>(parentData); case AssetType::MODEL:
{
const auto parentModel = reinterpret_cast<SHModelAsset*>(parentData);
for (auto i {0}; i < parent.subAssets.size(); ++i) for (auto i {0}; i < parent.subAssets.size(); ++i)
{ {
assetData.emplace( assetData.emplace(
@ -601,6 +667,25 @@ namespace SHADE
); );
} }
} }
break;
case AssetType::ANIM_CONTAINER:
{
const auto parentContainer = reinterpret_cast<SHAnimClipContainerAsset*>(parentData);
for (auto i {0}; i < parent.subAssets.size(); ++i)
{
assetData.emplace(
parent.subAssets[i]->id,
&parentContainer->clips[i]
);
}
}
break;
default:
SHLOG_WARNING("[Asset Manager] Parent type not supported to load sub assets, aborting...");
return nullptr;
}
return assetData[asset.id]; return assetData[asset.id];
} }
@ -757,6 +842,38 @@ namespace SHADE
return newAsset.id; return newAsset.id;
} }
else if(ext==ANIM_CONTAINER_EXTENSION)
{
SHAsset newAsset{
path.stem().string(),
GenerateAssetID(AssetType::ANIM_CONTAINER),
AssetType::ANIM_CONTAINER,
path,
false
};
assetCollection.emplace(newAsset.id, newAsset);
SHAnimClipContainerAsset* const data = reinterpret_cast<SHAnimClipContainerAsset*>(LoadData(newAsset));
assetData.emplace(newAsset.id, data);
for(auto& clip : data->clips)
{
SHAsset subAsset{
.name = clip.name,
.id = GenerateAssetID(AssetType::ANIM_CLIP),
.type = AssetType::ANIM_CLIP,
.isSubAsset = true,
.parent = newAsset.id
};
assetCollection.emplace(subAsset.id, subAsset);
assetCollection[newAsset.id].subAssets.push_back(&assetCollection[subAsset.id]);
assetData.emplace(subAsset.id, &clip);
}
SHAssetMetaHandler::WriteMetaData(assetCollection[newAsset.id]);
}
} }
void SHAssetManager::BuildAssetCollection() noexcept void SHAssetManager::BuildAssetCollection() noexcept
@ -813,7 +930,9 @@ namespace rttr
value("Material", AssetType::MATERIAL), value("Material", AssetType::MATERIAL),
value("Mesh", AssetType::MESH), value("Mesh", AssetType::MESH),
value("Script", AssetType::SCRIPT), value("Script", AssetType::SCRIPT),
value("Font", AssetType::FONT) value("Font", AssetType::FONT),
value("Animation Container", AssetType::ANIM_CONTAINER),
value("Animation Clip", AssetType::ANIM_CLIP)
); );
} }
} }

View File

@ -50,7 +50,8 @@ namespace SHADE
* \return const& to unordered_map<AssetName, AssetID> * \return const& to unordered_map<AssetName, AssetID>
****************************************************************************/ ****************************************************************************/
static std::vector<SHAsset> GetAllAssets() noexcept; static std::vector<SHAsset> GetAllAssets() noexcept;
static std::optional<SHAsset> GetAsset(AssetID id) noexcept; static SHAsset* GetAsset(AssetID id) noexcept;
static SHAsset const* GetAssetConst(AssetID id) noexcept;
static AssetType GetType(AssetID id) noexcept; static AssetType GetType(AssetID id) noexcept;
@ -63,6 +64,7 @@ namespace SHADE
* \return resource id generated for new asset * \return resource id generated for new asset
****************************************************************************/ ****************************************************************************/
static AssetID CreateNewAsset(AssetType type, AssetName name) noexcept; static AssetID CreateNewAsset(AssetType type, AssetName name) noexcept;
static AssetID CreateNewSubAsset(AssetType type, AssetName name, AssetID parent);
static bool SaveAsset(AssetID id) noexcept; static bool SaveAsset(AssetID id) noexcept;
static bool DeleteAsset(AssetID id) noexcept; static bool DeleteAsset(AssetID id) noexcept;

View File

@ -0,0 +1,951 @@
/************************************************************************************//*!
\file SHAnimationControllerEditor.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 1, 2023
\brief Contains the definition of SHAnimationControllerEditor's functions.
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.
*//*************************************************************************************/
#include "SHpch.h"
#include "SHAnimationControllerEditor.h"
// STL Includes
#include <format>
// External Dependencies
#include <imgui.h>
#include <imnodes.h>
// Project Includes
#include "Editor/IconsMaterialDesign.h"
#include "Animation/SHAnimationController.h"
#include "Editor/SHEditorUI.h"
#include "Editor/SHEditorWidgets.hpp"
#include "Editor/Command/SHCommand.hpp"
#include "Input/SHInputManager.h"
#include "Resource/SHResourceManager.h"
#include "Editor/EditorWindow/SHEditorWindowManager.h"
#include "Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*-----------------------------------------------------------------------------------*/
SHAnimationControllerEditor::SHAnimationControllerEditor()
: SHEditorWindow("Animation Controller Editor", ImGuiWindowFlags_MenuBar)
{}
/*-----------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*-----------------------------------------------------------------------------------*/
void SHAnimationControllerEditor::Init()
{
SHEditorWindow::Init();
// Set up caches
conditionsList =
{
"None",
"=",
"!=",
"<",
"<=",
">",
">="
};
typesList =
{
"Boolean",
"Trigger",
"Float",
"Integer"
};
// Set up sample animation controller for testing
SHAnimationController controller;
auto n1 = controller.CreateNode();
auto n2 = controller.CreateNode();
auto n3 = controller.CreateNode();
n1->Name = "N1";
n2->Name = "N2";
n3->Name = "N3";
SHAnimationController::Transition t;
t.Target = n3;
n1->Transitions.emplace_back(t);
n2->Transitions.emplace_back(t);
t.Target = n1;
n3->Transitions.emplace_back(t);
Open(controller);
}
void SHAnimationControllerEditor::Update()
{
SHEditorWindow::Update();
if (Begin())
{
// Only render the node editor if there is controller data
if (controllerData.has_value())
{
// Calculate size of each portion
const float WINDOW_WIDTH = ImGui::GetWindowSize().x;
const float MAIN_PANEL_COLUMN_WIDTH = WINDOW_WIDTH * 0.7f;
const float SIDE_PANELS_COLUMN_WIDTH = (WINDOW_WIDTH - MAIN_PANEL_COLUMN_WIDTH) * 0.5f;
// Draw
drawActiveMenuBar();
ImGui::BeginTable("base_table", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable);
{
// Set up Columns
ImGui::TableSetupColumn(" Parameters", ImGuiTableColumnFlags_WidthStretch, SIDE_PANELS_COLUMN_WIDTH);
ImGui::TableSetupColumn("State Machine", ImGuiTableColumnFlags_WidthFixed, MAIN_PANEL_COLUMN_WIDTH);
ImGui::TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch, SIDE_PANELS_COLUMN_WIDTH);
// Header
ImGui::TableHeadersRow();
// Render menu bars
ImGui::TableNextRow();
{
ImGui::TableSetColumnIndex(0);
drawParamsMenuBar();
ImGui::TableSetColumnIndex(1);
drawNodeEditorMenuBar();
ImGui::TableSetColumnIndex(2);
drawPropertiesMenuBar();
}
// Render contents
ImGui::TableNextRow();
{
ImGui::TableSetColumnIndex(0);
drawParamsPanel();
ImGui::TableSetColumnIndex(1);
drawNodeEditor();
ImGui::TableSetColumnIndex(2);
drawPropertiesPanel();
}
}
ImGui::EndTable();
}
else
{
SHEditorUI::CenteredText("No animation controller is selected.");
}
}
ImGui::End();
}
void SHAnimationControllerEditor::Exit()
{
SHEditorWindow::Exit();
}
void SHAnimationControllerEditor::Open(SHAnimationController& controllerHandle)
{
controller = controllerHandle;
controllerData = deserialise(controller);
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
void SHAnimationControllerEditor::drawActiveMenuBar()
{
if (ImGui::BeginMenuBar())
{
// Save Button
if (ImGui::Button(std::format("{} Save", ICON_MD_SAVE).data()))
{
controller = serialise(controllerData.value()); // TODO: Actually save the resource
}
// Discard Button
if (ImGui::Button(std::format("{} Discard Changes", ICON_MD_CANCEL).data()))
{
Open(controller); // TODO: Actually load the resource
}
ImGui::EndMenuBar();
}
}
void SHAnimationControllerEditor::drawParamsMenuBar()
{
// Add Parameter Button
if (ImGui::BeginCombo("##Type", std::format("{} Add Parameter", ICON_MD_ADD).data(), ImGuiComboFlags_None))
{
// All other options
for (int i = 0; i < static_cast<int>(typesList.size()); ++i)
{
if (ImGui::Selectable(typesList[i].c_str()))
{
int count = 0;
std::string paramName = "New Param";
while (controllerData->Params.contains(paramName))
{
paramName = "New Param " + std::to_string(++count);
}
controllerData->Params.emplace(paramName, static_cast<SHAnimationController::AnimParam::Type>(i));
}
}
ImGui::EndCombo();
}
}
void SHAnimationControllerEditor::drawParamsPanel()
{
int paramId = 0;
for (const auto& param : controllerData->Params)
{
ImGui::PushID(paramId++);
if (SHEditorWidgets::InputText
(
"",
[&]() { return param.first; },
[&](const std::string& val)
{
// Remove from previous
const SHAnimationController::AnimParam::Type TYPE = param.second;
controllerData->Params.erase(param.first);
// Put into the new
controllerData->Params[val] = TYPE;
// Update all links
for (auto& link : controllerData->Links)
{
link.second.ParamName = val;
}
},
{}, ImGuiInputTextFlags_EnterReturnsTrue
))
{
ImGui::PopID();
break; // Map was modified
}
ImGui::SameLine();
if (ImGui::BeginCombo("##Type", typesList[static_cast<int>(param.second)].c_str(), ImGuiComboFlags_None))
{
// All other options
for (int i = 0; i < static_cast<int>(typesList.size()); ++i)
{
const bool IS_SELECTED = static_cast<int>(param.second) == i;
if (ImGui::Selectable(typesList[i].c_str(), IS_SELECTED))
{
SHCommandManager::PerformCommand
(
std::reinterpret_pointer_cast<SHBaseCommand>
(
std::make_shared<SHCommand<SHAnimationController::AnimParam::Type>>
(
param.second,
static_cast<SHAnimationController::AnimParam::Type>(i),
[&](SHAnimationController::AnimParam::Type val)
{
controllerData->Params[param.first] = val;
// TODO: This needs to be handled in a custom command
// For changing to boolean, we need to change inequalities to not equal, etc.
if (val == SHAnimationController::AnimParam::Type::Bool)
{
for (auto& link : controllerData->Links)
{
switch (link.second.Condition)
{
case SHAnimationController::Transition::ConditionType::GreaterThan:
case SHAnimationController::Transition::ConditionType::LessThan:
link.second.Condition = SHAnimationController::Transition::ConditionType::NotEquals;
break;
case SHAnimationController::Transition::ConditionType::GreaterThanOrEqual:
case SHAnimationController::Transition::ConditionType::LessThanOrEqual:
link.second.Condition = SHAnimationController::Transition::ConditionType::Equals;
break;
}
}
}
}
)
),
false
);
}
if (IS_SELECTED)
{
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
ImGui::PopID();
}
}
void SHAnimationControllerEditor::drawNodeEditorMenuBar()
{
// Add Node Button
if (ImGui::Button(std::format("{} Add Node", ICON_MD_ADD).data()))
{
createNode(controllerData.value());
}
ImGui::SameLine();
// Delete Node Button
ImGui::BeginDisabled((ImNodes::NumSelectedNodes() + ImNodes::NumSelectedLinks()) < 1);
if (ImGui::Button(std::format("{} Delete Objects", ICON_MD_DELETE).data()))
{
deleteSelectedLinks();
deleteSelectedNodes();
}
ImGui::EndDisabled();
ImGui::SameLine();
// Set Starting Node Button
ImGui::BeginDisabled(ImNodes::NumSelectedNodes() != 1);
if (ImGui::Button(std::format("{} Set Starting Node", ICON_MD_HOME).data()))
{
// Get id of selected node
int selectedNode = 0;
ImNodes::GetSelectedNodes(&selectedNode);
controllerData->StartingNode = selectedNode; // We can do this as the ImNodes node index is the same
}
ImGui::EndDisabled();
}
void SHAnimationControllerEditor::drawNodeEditor()
{
static constexpr float NODE_WIDTH = 80.0f;
static constexpr float TEXT_FIELD_PADDING = 15.0f;
ImNodes::BeginNodeEditor();
{
/* Draw Nodes */
for (auto& node : controllerData->Nodes)
{
// Draw the node
ImNodes::BeginNode(node.Index);
{
// Title
ImNodes::BeginNodeTitleBar();
{
// Starting node marker
if (node.Index == controllerData->StartingNode)
{
const float INDENT = NODE_WIDTH * 0.6f;
ImGui::Indent(INDENT);
ImGui::Text(ICON_MD_HOME);
ImGui::Unindent(INDENT);
}
if (node.EditingName)
{
if (ImGui::Button(ICON_MD_DONE))
{
node.EditingName = false;
}
ImGui::SameLine();
ImGui::SetNextItemWidth(std::max(ImGui::CalcTextSize(node.Name.c_str()).x + TEXT_FIELD_PADDING, NODE_WIDTH));
SHEditorUI::InputTextField("", node.Name);
}
else
{
if (ImGui::Button(ICON_MD_EDIT))
{
node.EditingName = true;
}
ImGui::SameLine();
ImGui::Text(node.Name.c_str());
}
}
ImNodes::EndNodeTitleBar();
// Body
const auto CLIP_NAME = SHResourceManager::GetAssetName<SHAnimationClip>(node.Clip).value_or("");
ImGui::SetNextItemWidth(NODE_WIDTH);
SHEditorWidgets::DragDropReadOnlyField<AssetID>
(
"Clip", CLIP_NAME,
[&]()
{
return SHResourceManager::GetAssetID<SHAnimationClip>(node.Clip).value_or(0);
},
[&](AssetID id)
{
if (SHAssetManager::GetType(id) != AssetType::ANIM_CLIP)
return;
node.Clip = SHResourceManager::LoadOrGet<SHAnimationClip>(id);
SHResourceManager::FinaliseChanges();
},
SHDragDrop::DRAG_RESOURCE, {}, NODE_WIDTH
);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
if (node.Clip)
{
AssetID assetID = SHResourceManager::GetAssetID<SHAnimationClip>(node.Clip).value_or(0);
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID);
}
}
// Input Nodes
for (auto inputAttrib : node.InputAttribs)
{
drawInputNode(inputAttrib.Raw, ImNodesPinShape_CircleFilled);
}
// Render an extra input
drawInputNode(getExtraInputAttrib(node.Index).Raw, ImNodesPinShape_Circle);
// Output Nodes
for (auto outputAttrib : node.OutputAttribs)
{
drawOutputNode(outputAttrib.Raw, node.Index, ImNodesPinShape_TriangleFilled);
}
// Render an extra output
drawOutputNode(getExtraOutputAttrib(node.Index).Raw, node.Index, ImNodesPinShape_Triangle);
}
ImNodes::EndNode();
}
// Draw links
for (auto link : controllerData->Links)
{
ImNodes::Link(link.first, link.second.SourceAttrib.Raw, link.second.DestAttrib.Raw);
}
}
ImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_BottomRight);
ImNodes::EndNodeEditor();
int sourceAttrib, destAttrib;
if (ImNodes::IsLinkCreated(&sourceAttrib, &destAttrib))
{
// Get the two indices
NodeAttributeIndex sourceAttribIndex, destAttribIndex;
sourceAttribIndex.Raw = static_cast<uint16_t>(sourceAttrib);
destAttribIndex.Raw = static_cast<uint16_t>(destAttrib);
// Ensure that we can access the nodes
if (controllerData->IndexToNodeMap.contains(sourceAttribIndex.OwnerNodeIndex) &&
controllerData->IndexToNodeMap.contains(destAttribIndex.OwnerNodeIndex))
{
// Retrieve the nodes
auto inputNodeIter = controllerData->IndexToNodeMap[sourceAttribIndex.OwnerNodeIndex];
auto outputNodeIter = *controllerData->IndexToNodeMap[destAttribIndex.OwnerNodeIndex];
// Create link
createLink
(
controllerData.value(),
controllerData->IndexToNodeMap[sourceAttribIndex.OwnerNodeIndex],
controllerData->IndexToNodeMap[destAttribIndex.OwnerNodeIndex]
);
}
}
// Delete
if (SHInputManager::GetKeyUp(SHInputManager::SH_KEYCODE::DEL))
{
deleteSelectedLinks();
deleteSelectedNodes();
}
}
void SHAnimationControllerEditor::drawPropertiesMenuBar()
{
// Set Starting Node Button
const int SELECTED_LINKS_COUNT = ImNodes::NumSelectedLinks();
ImGui::BeginDisabled(SELECTED_LINKS_COUNT < 1);
if (ImGui::Button(std::format("{} Reset Conditions", ICON_MD_SETTINGS_BACKUP_RESTORE).data()))
{
std::vector<int> selectedLinks(SELECTED_LINKS_COUNT);
ImNodes::GetSelectedLinks(selectedLinks.data());
for (auto& link : selectedLinks)
{
// Get LinkData
NodeLinkIndex nodeLinkIndex;
nodeLinkIndex.Raw = link;
if (!controllerData->Links.contains(nodeLinkIndex.Raw))
continue;
LinkData& linkData = controllerData->Links[nodeLinkIndex.Raw];
// Ensure that the link is valid
if (!controllerData->IndexToNodeMap.contains(nodeLinkIndex.SourceAttribute.OwnerNodeIndex) ||
!controllerData->IndexToNodeMap.contains(nodeLinkIndex.DestinationAttribute.OwnerNodeIndex))
{
continue;
}
linkData.ParamName = "";
linkData.Condition = SHAnimationController::Transition::ConditionType::None;
}
}
ImGui::EndDisabled();
}
void SHAnimationControllerEditor::drawPropertiesPanel()
{
const int SELECTED_LINKS_COUNT = ImNodes::NumSelectedLinks();
if (SELECTED_LINKS_COUNT > 0)
{
std::vector<int> selectedLinks(SELECTED_LINKS_COUNT);
ImNodes::GetSelectedLinks(selectedLinks.data());
// Go through all links and display them
int index = 0;
for (int link : selectedLinks)
{
// Get LinkData
NodeLinkIndex nodeLinkIndex;
nodeLinkIndex.Raw = link;
if (!controllerData->Links.contains(nodeLinkIndex.Raw))
continue;
LinkData& linkData = controllerData->Links[nodeLinkIndex.Raw];
// Ensure that the link is valid
if (!controllerData->IndexToNodeMap.contains(nodeLinkIndex.SourceAttribute.OwnerNodeIndex) ||
!controllerData->IndexToNodeMap.contains(nodeLinkIndex.DestinationAttribute.OwnerNodeIndex))
{
continue;
}
// Create name of the link
std::ostringstream oss;
oss << controllerData->IndexToNodeMap[nodeLinkIndex.SourceAttribute.OwnerNodeIndex]->Name
<< " " << ICON_MD_ARROW_RIGHT_ALT << " "
<< controllerData->IndexToNodeMap[nodeLinkIndex.DestinationAttribute.OwnerNodeIndex]->Name;
ImGui::PushID(index++);
// Display each link
if (SHEditorUI::CollapsingHeader(oss.str()))
{
const bool IS_PARAM_SET = !linkData.ParamName.empty();
// Anim Parameter
ImGui::Text("Parameter");
ImGui::SameLine();
if (ImGui::BeginCombo("##Parameter", IS_PARAM_SET ? linkData.ParamName.c_str() : "None", ImGuiComboFlags_None))
{
// Initial "None" option
if (ImGui::Selectable("None", !IS_PARAM_SET))
{
SHCommandManager::PerformCommand
(
std::reinterpret_pointer_cast<SHBaseCommand>
(
std::make_shared<SHCommand<std::string>>
(
linkData.ParamName,
std::string{},
[&](const std::string& val) { linkData.ParamName = val; }
)
),
false
);
}
if (!IS_PARAM_SET)
{
ImGui::SetItemDefaultFocus();
}
// All other options
for (const auto& param : controllerData->Params)
{
const bool IS_SELECTED = param.first == linkData.ParamName;
if (ImGui::Selectable(param.first.c_str(), IS_SELECTED))
{
SHCommandManager::PerformCommand
(
std::reinterpret_pointer_cast<SHBaseCommand>
(
std::make_shared<SHCommand<std::string>>
(
linkData.ParamName,
param.first,
[&](const std::string& val) { linkData.ParamName = val; }
)
),
false
);
}
if (IS_SELECTED)
{
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
// Properties for an Animation Parameter
if (IS_PARAM_SET && controllerData->Params.contains(linkData.ParamName))
{
const SHAnimationController::AnimParam::Type PARAM_TYPE = controllerData->Params[linkData.ParamName];
if (PARAM_TYPE != SHAnimationController::AnimParam::Type::Trigger)
{
// Comparison Type
const auto& CURR_COMPARISON = conditionsList[static_cast<int>(linkData.Condition)];
ImGui::Text("Condition Type");
ImGui::SameLine();
if (ImGui::BeginCombo("##ConditionType", CURR_COMPARISON.c_str(), ImGuiComboFlags_None))
{
// We only show equal and not equal for bool
const int LAST_ELEM = PARAM_TYPE == SHAnimationController::AnimParam::Type::Bool ? static_cast<int>(SHAnimationController::Transition::ConditionType::NotEquals)
: static_cast<int>(conditionsList.size() - 1);
// Comparisons
for (int i = 0; i <= LAST_ELEM; ++i)
{
const bool IS_SELECTED = i == static_cast<int>(linkData.Condition);
if (ImGui::Selectable(conditionsList[i].c_str(), IS_SELECTED))
{
SHCommandManager::PerformCommand
(
std::reinterpret_pointer_cast<SHBaseCommand>
(
std::make_shared<SHCommand<SHAnimationController::Transition::ConditionType>>
(
linkData.Condition,
static_cast<SHAnimationController::Transition::ConditionType>(i),
[&](SHAnimationController::Transition::ConditionType val) { linkData.Condition = val; }
)
),
false
);
}
if (IS_SELECTED)
{
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
// Parameter Value
if (linkData.Condition != SHAnimationController::Transition::ConditionType::None)
{
switch (PARAM_TYPE)
{
case SHAnimationController::AnimParam::Type::Bool:
SHEditorWidgets::CheckBox
(
"Required State",
[&]() { return linkData.ParamThresholdValue != 0.0f; },
[&](bool val) { linkData.ParamThresholdValue = static_cast<float>(val); }
);
break;
case SHAnimationController::AnimParam::Type::Float:
SHEditorWidgets::DragFloat
(
"Threshold",
[&]() { return linkData.ParamThresholdValue; },
[&](float val) { linkData.ParamThresholdValue = val; }
);
break;
case SHAnimationController::AnimParam::Type::Int:
SHEditorWidgets::DragInt
(
"Threshold",
[&]() { return static_cast<int>(linkData.ParamThresholdValue); },
[&](int val) { linkData.ParamThresholdValue = static_cast<float>(val); }
);
break;
}
}
}
}
}
ImGui::PopID();
}
}
else
{
ImGui::Text("Select an object to view properties.");
}
}
SHAnimationControllerEditor::NodeAttributeIndex SHAnimationControllerEditor::getExtraInputAttrib(uint32_t nodeIndex)
{
NodeAttributeIndex extraInputAttrib;
extraInputAttrib.OwnerNodeIndex = nodeIndex;
extraInputAttrib.AttributeIndex = std::numeric_limits<int8_t>::lowest();
return extraInputAttrib;
}
SHAnimationControllerEditor::NodeAttributeIndex SHAnimationControllerEditor::getExtraOutputAttrib(uint32_t nodeIndex)
{
NodeAttributeIndex extraOutputAttrib;
extraOutputAttrib.OwnerNodeIndex = nodeIndex;
extraOutputAttrib.AttributeIndex = std::numeric_limits<int8_t>::max();
return extraOutputAttrib;
}
void SHAnimationControllerEditor::drawInputNode(int id, ImNodesPinShape_ pinShape)
{
ImNodes::BeginInputAttribute(id, pinShape);
ImGui::Text("Input");
ImNodes::EndInputAttribute();
}
void SHAnimationControllerEditor::drawOutputNode(int id, int parentNodeId, ImNodesPinShape_ pinShape)
{
static char const* TITLE = "Output";
static float RIGHT_PADDING = 20.0f;
ImNodes::BeginOutputAttribute(id, ImNodesPinShape_TriangleFilled);
ImGui::Indent(ImNodes::GetNodeDimensions(parentNodeId).x - ImGui::CalcTextSize(TITLE).x - RIGHT_PADDING);
ImGui::Text(TITLE);
ImNodes::EndOutputAttribute();
}
void SHAnimationControllerEditor::deleteSelectedNodes()
{
const int NUM_SELECTED_NODES= ImNodes::NumSelectedNodes();
if (NUM_SELECTED_NODES > 0)
{
std::vector<int> selectedNodes(NUM_SELECTED_NODES);
ImNodes::GetSelectedNodes(selectedNodes.data());
for (auto nodeId : selectedNodes)
{
deleteNode(controllerData.value(), nodeId);
}
}
}
void SHAnimationControllerEditor::deleteSelectedLinks()
{
const int NUM_SELECTED_LINKS = ImNodes::NumSelectedLinks();
if (NUM_SELECTED_LINKS > 0)
{
std::vector<int> selectedLinks(NUM_SELECTED_LINKS);
ImNodes::GetSelectedLinks(selectedLinks.data());
for (auto linkId : selectedLinks)
{
deleteLink(controllerData.value(), linkId);
}
}
}
std::list<SHAnimationControllerEditor::Node>::iterator SHAnimationControllerEditor::createNode(AnimControllerData& data)
{
const NodeIndex NEW_NODE_IDX = data.NextNodeIndex++;
Node localNode;
localNode.Index = NEW_NODE_IDX;
data.Nodes.emplace_back(std::move(localNode));
// Update the node map
auto nodeIter = --data.Nodes.end();
data.IndexToNodeMap[NEW_NODE_IDX] = nodeIter;
return nodeIter;
}
SHAnimationControllerEditor::LinkMap::iterator SHAnimationControllerEditor::createLink(AnimControllerData& data, std::list<Node>::iterator sourceNode, std::list<Node>::iterator destNode)
{
// Update source node's output attributes
NodeAttributeIndex attribIndex;
attribIndex.OwnerNodeIndex = sourceNode->Index;
attribIndex.AttributeIndex = static_cast<int8_t>(sourceNode->OutputAttribs.size() + 1);
sourceNode->OutputAttribs.emplace_back(attribIndex);
// Update target node's input attributes
attribIndex.OwnerNodeIndex = destNode->Index;
attribIndex.AttributeIndex = static_cast<int8_t>(-(destNode->InputAttribs.size() + 1));
destNode->InputAttribs.emplace_back(attribIndex);
// Create link
LinkData link;
link.SourceNode = sourceNode;
link.TargetNode = destNode;
link.SourceAttrib = sourceNode->OutputAttribs.back();
link.DestAttrib = destNode->InputAttribs.back();
NodeLinkIndex linkIdx;
linkIdx.SourceAttribute = link.SourceAttrib;
linkIdx.DestinationAttribute = link.DestAttrib;
const auto EMPLACE_DATA = data.Links.emplace(linkIdx.Raw, std::move(link));
sourceNode->Transitions.emplace_back(linkIdx);
return EMPLACE_DATA.first;
}
void SHAnimationControllerEditor::deleteLink(AnimControllerData& data, LinkIndex link)
{
const NodeLinkIndex LINK_IDX { link };
// Error check, don't do anything if they don't exist
if (!data.IndexToNodeMap.contains(LINK_IDX.SourceAttribute.OwnerNodeIndex) ||
!data.IndexToNodeMap.contains(LINK_IDX.DestinationAttribute.OwnerNodeIndex))
return;
// Get source node and attributes
auto& sourceNode = *data.IndexToNodeMap[LINK_IDX.SourceAttribute.OwnerNodeIndex];
auto& destNode = *data.IndexToNodeMap[LINK_IDX.DestinationAttribute.OwnerNodeIndex];
// Remove attributes
std::erase(sourceNode.OutputAttribs, LINK_IDX.SourceAttribute);
std::erase(destNode.InputAttribs, LINK_IDX.DestinationAttribute);
// Remove link
std::erase(sourceNode.Transitions, LINK_IDX);
data.Links.erase(link);
}
void SHAnimationControllerEditor::deleteNode(AnimControllerData& data, NodeIndex nodeIndex)
{
// Get node to delete
if (!data.IndexToNodeMap.contains(nodeIndex))
return;
auto nodeToDeleteIter = data.IndexToNodeMap[nodeIndex];
// Remove all links to other nodes
for (auto link : nodeToDeleteIter->Transitions)
{
deleteLink(data, link.Raw);
}
// Remove all links from other nodes
for (auto node : data.Nodes)
{
for (NodeLinkIndex link : node.Transitions)
{
if (link.DestinationAttribute.OwnerNodeIndex == nodeIndex)
{
deleteLink(data, link.Raw);
}
}
}
// Then finally, delete this node
data.IndexToNodeMap.erase(nodeIndex);
data.Nodes.erase(nodeToDeleteIter);
// If the starting node was this node, we need to reassign
if (data.StartingNode == nodeIndex)
{
data.StartingNode = data.Nodes.empty() ? data.NextNodeIndex : data.Nodes.front().Index;
}
}
/*-----------------------------------------------------------------------------------*/
/* Static Helper Functions */
/*-----------------------------------------------------------------------------------*/
SHAnimationControllerEditor::AnimControllerData SHAnimationControllerEditor::deserialise(const SHAnimationController& controller)
{
AnimControllerData data;
// Maps controller nodes to data nodes
std::unordered_map<Handle<SHAnimationController::Node>, std::list<Node>::iterator> nodeMap;
// Load anim parameters
data.Params = controller.GetParams();
// Load nodes and links
for (auto node : controller.GetNodes())
{
auto localNode = createNode(data);
localNode->Name = node->Name;
localNode->Clip = node->Clip;
nodeMap.emplace(node, localNode);
}
// Load links
for (auto node : controller.GetNodes())
{
// Get the corresponding data node
auto dataNodeIter = nodeMap[node];
for (auto transition : node->Transitions)
{
// Invalid node check
if (!nodeMap.contains(transition.Target))
continue;
// Get the target node
auto targetNodeIter = nodeMap[transition.Target];
// Create link
auto& linkData = createLink(data, dataNodeIter, targetNodeIter)->second;
linkData.Condition = transition.Condition;
linkData.ParamName = transition.ParamName;
linkData.ParamThresholdValue = transition.Param.Value;
}
}
// Mark starting node
if (nodeMap.contains(controller.StartingNode))
{
data.StartingNode = nodeMap[controller.StartingNode]->Index;
}
return data;
}
SHAnimationController SHAnimationControllerEditor::serialise(const AnimControllerData& data)
{
SHAnimationController controller;
// Maps data nodes to controller nodes
std::unordered_map<NodeIndex, Handle<SHAnimationController::Node>> nodeMap;
// Create all nodes first
for (const auto& node : data.Nodes)
{
auto newNode = controller.CreateNode();
newNode->Name = node.Name;
newNode->Clip = node.Clip;
nodeMap[node.Index] = newNode;
}
// Create links
for (const auto& node : data.Nodes)
{
// Get controller node
auto controllerNode = nodeMap[node.Index];
for (auto link : node.Transitions)
{
// Ignore invalid link
if (!nodeMap.contains(link.SourceAttribute.OwnerNodeIndex) || !nodeMap.contains(link.DestinationAttribute.OwnerNodeIndex))
continue;
// Get link data
const LinkData& LINK_DATA = data.Links.at(link.Raw);
SHAnimationController::Transition transition;
transition.Target = nodeMap[link.DestinationAttribute.OwnerNodeIndex];
if (data.Params.contains(LINK_DATA.ParamName))
{
transition.Condition = LINK_DATA.Condition;
transition.ParamName = LINK_DATA.ParamName;
transition.Param.ParamType = data.Params.at(LINK_DATA.ParamName);
transition.Param.Value = LINK_DATA.ParamThresholdValue;
}
controllerNode->Transitions.emplace_back(std::move(transition));
}
}
// Starting Node
if (nodeMap.contains(data.StartingNode))
{
controller.StartingNode = nodeMap[data.StartingNode];
}
// Parameters
for (auto param : data.Params)
{
controller.AddParameter(param.first, param.second);
}
return controller;
}
}

View File

@ -0,0 +1,151 @@
/************************************************************************************//*!
\file SHAnimationControllerEditor.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 1, 2023
\brief Contains the definition of SHAnimationControllerEditor.
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
// STL Includes
#include <vector>
#include <optional>
// External Dependencies
#include <imnodes.h>
// Project Includes
#include "Resource/SHHandle.h"
#include "Editor/EditorWindow/SHEditorWindow.h"
#include "Animation/SHAnimationController.h"
namespace SHADE
{
/// <summary>
/// Editor for modifying the Animation Controller state machine.
/// </summary>
class SHAnimationControllerEditor final : public SHEditorWindow
{
public:
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*---------------------------------------------------------------------------------*/
SHAnimationControllerEditor();
~SHAnimationControllerEditor() = default;
/*---------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*---------------------------------------------------------------------------------*/
void Init() override;
void Update() override;
void Exit() override;
/*---------------------------------------------------------------------------------*/
/* Usage Functions */
/*---------------------------------------------------------------------------------*/
void Open(SHAnimationController& controller);
private:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
using NodeIndex = uint8_t;
using LinkIndex = int32_t;
union NodeAttributeIndex
{
uint16_t Raw;
struct
{
NodeIndex OwnerNodeIndex;
int8_t AttributeIndex; // Negative is input, positive is output
};
bool operator==(NodeAttributeIndex rhs) const noexcept { return Raw == rhs.Raw; }
};
union NodeLinkIndex
{
LinkIndex Raw;
struct
{
NodeAttributeIndex SourceAttribute;
NodeAttributeIndex DestinationAttribute;
};
bool operator==(NodeLinkIndex rhs) const noexcept { return Raw != rhs.Raw; }
};
struct Node
{
NodeIndex Index;
std::string Name = "Unnamed Node";
Handle<SHAnimationClip> Clip;
std::vector<NodeAttributeIndex> InputAttribs;
std::vector<NodeAttributeIndex> OutputAttribs;
std::vector<NodeLinkIndex> Transitions;
bool EditingName = false;
};
struct LinkData
{
// Source/Dest Data
std::list<Node>::iterator SourceNode;
std::list<Node>::iterator TargetNode;
NodeAttributeIndex SourceAttrib;
NodeAttributeIndex DestAttrib;
// Conditional Data
SHAnimationController::Transition::ConditionType Condition = SHAnimationController::Transition::ConditionType::None;
std::string ParamName;
SHAnimationController::AnimParam::ValueType ParamThresholdValue;
};
using LinkMap = std::unordered_map<LinkIndex, LinkData>;
struct AnimControllerData
{
NodeIndex StartingNode = 0;
std::list<Node> Nodes;
std::unordered_map<std::string, SHAnimationController::AnimParam::Type> Params;
LinkMap Links;
int NextNodeIndex = 0; // Index to use for newly created nodes
std::unordered_map<NodeIndex, std::list<Node>::iterator> IndexToNodeMap;
};
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
SHAnimationController controller;
std::optional<AnimControllerData> controllerData;
// Persistent Cached Data
std::vector<std::string> conditionsList;
std::vector<std::string> typesList;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
void drawActiveMenuBar();
void drawParamsMenuBar();
void drawParamsPanel();
void drawNodeEditorMenuBar();
void drawNodeEditor();
void drawPropertiesMenuBar();
void drawPropertiesPanel();
NodeAttributeIndex getExtraInputAttrib(uint32_t nodeIndex);
NodeAttributeIndex getExtraOutputAttrib(uint32_t nodeIndex);
void drawInputNode(int id, ImNodesPinShape_ pinShape);
void drawOutputNode(int id, int parentNodeId, ImNodesPinShape_ pinShape);
void deleteSelectedNodes();
void deleteSelectedLinks();
/*---------------------------------------------------------------------------------*/
/* Static Helper Functions */
/*---------------------------------------------------------------------------------*/
static std::list<Node>::iterator createNode(AnimControllerData& data);
static LinkMap::iterator createLink(AnimControllerData& data, std::list<Node>::iterator sourceNode, std::list<Node>::iterator destNode);
static void deleteLink(AnimControllerData& data, LinkIndex link);
static void deleteNode(AnimControllerData& data, NodeIndex nodeIndex);
static AnimControllerData deserialise(const SHAnimationController& controller);
static SHAnimationController serialise(const AnimControllerData& data);
};
}

View File

@ -22,7 +22,11 @@
#include "Assets/Asset Types/SHPrefabAsset.h" #include "Assets/Asset Types/SHPrefabAsset.h"
#include "Serialization/SHSerialization.h" #include "Serialization/SHSerialization.h"
#include <Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h> #include <Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h>
#include "Assets/Asset Types/SHAnimClipContainerAsset.h"
#include "Assets/Asset Types/Models/SHModelAsset.h"
#include "Serialization/Prefab/SHPrefabManager.h" #include "Serialization/Prefab/SHPrefabManager.h"
#include "Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h"
namespace SHADE namespace SHADE
{ {
@ -371,6 +375,7 @@ namespace SHADE
case AssetType::SHADER: break; case AssetType::SHADER: break;
case AssetType::SHADER_BUILT_IN: break; case AssetType::SHADER_BUILT_IN: break;
case AssetType::TEXTURE: break; case AssetType::TEXTURE: break;
case AssetType::MODEL: break;
case AssetType::MESH: break; case AssetType::MESH: break;
case AssetType::SCENE: case AssetType::SCENE:
{ {
@ -395,6 +400,12 @@ namespace SHADE
scriptEngine->OpenFile(asset->path); scriptEngine->OpenFile(asset->path);
} }
break; break;
case AssetType::ANIM_CONTAINER:
if (auto animInspector = SHEditorWindowManager::GetEditorWindow<SHRawAnimInspector>())
{
animInspector->Open(asset->id);
}
break;
case AssetType::MAX_COUNT: break; case AssetType::MAX_COUNT: break;
default:; default:;
} }
@ -480,6 +491,48 @@ namespace SHADE
isAssetBeingCreated = false; isAssetBeingCreated = false;
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
{
auto const models {SHAssetManager::GetAllRecordOfType(AssetType::MODEL)};
ImGui::RadioButton("Animation Clip Container", true);
ImGui::SameLine();
static char const* const modelPrompt = "Select a model with animations";
char const* currentItem = modelPrompt;
AssetID selected {0};
if (ImGui::BeginCombo("##combo", currentItem, ImGuiComboFlags_None))
{
for (auto const& model : models)
{
bool isSelected = currentItem == model.name;
if (ImGui::Selectable(model.name.data(), isSelected))
{
auto const data {SHAssetManager::GetConstData<SHModelAsset>(model.id)};
if (!data->anims.empty())
{
const auto animContainerId = SHAssetManager::CreateNewAsset(AssetType::ANIM_CONTAINER, model.name + "Anims");
auto data = SHAssetManager::GetData<SHAnimClipContainerAsset>(animContainerId);
data->animRawDataAssetId = model.id;
SHAssetManager::SaveAsset(animContainerId);
if (auto animInspector = SHEditorWindowManager::GetEditorWindow<SHRawAnimInspector>())
{
animInspector->Open(animContainerId);
}
QueueRefresh();
isAssetBeingCreated = false;
ImGui::CloseCurrentPopup();
}
}
if (isSelected)
{
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
}
ImGui::EndPopup(); ImGui::EndPopup();
} }
//if (ImGui::BeginMenu("Create Asset")) //if (ImGui::BeginMenu("Create Asset"))

View File

@ -30,6 +30,7 @@
#include "../SHEditorWindowManager.h" #include "../SHEditorWindowManager.h"
#include "../AssetBrowser/SHAssetBrowser.h" #include "../AssetBrowser/SHAssetBrowser.h"
#include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h"
#include "Animation/SHAnimationClip.h"
namespace SHADE namespace SHADE
{ {
@ -639,9 +640,13 @@ namespace SHADE
if (ImGui::CollapsingHeader(componentType.get_name().data())) if (ImGui::CollapsingHeader(componentType.get_name().data()))
{ {
DrawContextMenu(component); DrawContextMenu(component);
/* Animation Rig */
Handle<SHRig> const& rig = component->GetRig(); Handle<SHRig> const& rig = component->GetRig();
const auto RIG_NAME = rig ? SHResourceManager::GetAssetName<SHRig>(rig).value_or("") : ""; const auto RIG_NAME = rig ? SHResourceManager::GetAssetName<SHRig>(rig).value_or("") : "";
SHEditorWidgets::DragDropReadOnlyField<AssetID>("Rig", RIG_NAME, [component]() SHEditorWidgets::DragDropReadOnlyField<AssetID>
(
"Rig", RIG_NAME, [component]()
{ {
Handle<SHRig> const& rig = component->GetRig(); Handle<SHRig> const& rig = component->GetRig();
return SHResourceManager::GetAssetID<SHRig>(rig).value_or(0); return SHResourceManager::GetAssetID<SHRig>(rig).value_or(0);
@ -650,12 +655,14 @@ namespace SHADE
{ {
if (SHAssetManager::GetType(id) != AssetType::MODEL) if (SHAssetManager::GetType(id) != AssetType::MODEL)
{ {
SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") SHLOG_WARNING("Attempted to assign non mesh rig asset to Animator Rig property!")
return; return;
} }
component->SetRig(SHResourceManager::LoadOrGet<SHRig>(id)); component->SetRig(SHResourceManager::LoadOrGet<SHRig>(id));
SHResourceManager::FinaliseChanges(); SHResourceManager::FinaliseChanges();
}, SHDragDrop::DRAG_RESOURCE); },
SHDragDrop::DRAG_RESOURCE
);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{ {
if (Handle<SHRig> const& rig = component->GetRig()) if (Handle<SHRig> const& rig = component->GetRig())
@ -664,28 +671,34 @@ namespace SHADE
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID); SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID);
} }
} }
Handle<SHAnimationClip> const& clip = component->GetCurrentClip();
const auto CLIP_NAME = clip ? SHResourceManager::GetAssetName<SHAnimationClip>(clip).value_or("") : ""; /* Animation Controller */
SHEditorWidgets::DragDropReadOnlyField<AssetID>("Clip", CLIP_NAME, Handle<SHAnimationController> animController = component->GetAnimationController();
[component]() const auto AC_NAME = animController ? SHResourceManager::GetAssetName<SHAnimationController>(animController).value_or("") : "";
SHEditorWidgets::DragDropReadOnlyField<AssetID>
(
"Animation Controller", AC_NAME, [component]()
{ {
Handle<SHAnimationClip> const& clip = component->GetCurrentClip(); Handle<SHAnimationController> ac = component->GetAnimationController();
return SHResourceManager::GetAssetID<SHAnimationClip>(clip).value_or(0); return SHResourceManager::GetAssetID<SHAnimationController>(ac).value_or(0);
}, },
[component](AssetID const& id) [component](AssetID const& id)
{ {
if (SHAssetManager::GetType(id) != AssetType::MODEL) if (SHAssetManager::GetType(id) != AssetType::ANIM_CONTROLLER)
{ {
SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") SHLOG_WARNING("Attempted to assign non animation controller asset to Animator Animation Controller property!")
return; return;
} }
component->SetClip(SHResourceManager::LoadOrGet<SHAnimationClip>(id)); component->SetAnimationController(SHResourceManager::LoadOrGet<SHAnimationController>(id));
}, SHDragDrop::DRAG_RESOURCE); SHResourceManager::FinaliseChanges();
},
SHDragDrop::DRAG_RESOURCE
);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{ {
if (Handle<SHAnimationClip> const& clip = component->GetCurrentClip()) if (Handle<SHAnimationController> ac = component->GetAnimationController())
{ {
AssetID assetID = SHResourceManager::GetAssetID<SHAnimationClip>(clip).value_or(0); AssetID assetID = SHResourceManager::GetAssetID<SHAnimationController>(ac).value_or(0);
SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID); SHEditorWindowManager::GetEditorWindow<SHAssetBrowser>()->SetScrollTo(assetID);
} }
} }

View File

@ -0,0 +1,273 @@
/************************************************************************************//*!
\file SHRawAnimInspector.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 1, 2023
\brief Contains the definition of SHRawAnimInspector's functions.
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.
*//*************************************************************************************/
#include "SHpch.h"
#include "SHRawAnimInspector.h"
// STL Includes
#include <format>
// External Dependencies
#include <imgui.h>
#include <misc/cpp/imgui_stdlib.h>
// Project Includes
#include "Editor/IconsMaterialDesign.h"
#include "Animation/SHAnimationClip.h"
#include "Resource/SHResourceManager.h"
#include "Editor/EditorWindow/SHEditorWindowManager.h"
#include "Editor/SHEditorUI.h"
#include "Assets/SHAssetManager.h"
#include "Editor/SHEditorWidgets.hpp"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* SHAnimClipCreatePrompt - Constructors/Destructors */
/*-----------------------------------------------------------------------------------*/
SHAnimClipCreatePrompt::SHAnimClipCreatePrompt()
: SHPopUpWindow("Create Animation Clip", true, 0, 0) {}
/*---------------------------------------------------------------------------------*/
/* SHAnimClipCreatePrompt - Lifecycle Functions */
/*---------------------------------------------------------------------------------*/
void SHAnimClipCreatePrompt::Init(
SHAsset* contAsset,
SHAnimClipContainerAsset* cont,
Handle<SHRawAnimation> rawAnim,
std::function<void(AssetID)> onClose
)
{
containerAsset = contAsset;
container = cont;
rawAnimation = rawAnim;
// Set default parameters
if (rawAnimation)
{
newAssetName.clear();
firstIndex = 0;
lastIndex = rawAnimation->GetTotalFrames();
}
// Assign callback
this->onClose = onClose;
}
void SHAnimClipCreatePrompt::Draw()
{
if (Begin())
{
// Properties
SHEditorUI::InputTextField("Name", newAssetName);
SHEditorUI::PushID(0);
SHEditorUI::InputUnsignedInt("First Frame Index", firstIndex);
SHEditorUI::PopID();
SHEditorUI::PushID(1);
SHEditorUI::InputUnsignedInt("Last Frame Index", lastIndex);
SHEditorUI::PopID();
// Invalid values
const bool INVALID_CONFIG = newAssetName.empty() || firstIndex > lastIndex;
// Buttons
ImGui::BeginDisabled(INVALID_CONFIG);
{
if (ImGui::Button("Save"))
{
// Generate new asset
const AssetID NEW_ASSET_ID = SHAssetManager::CreateNewSubAsset(AssetType::ANIM_CLIP, newAssetName, containerAsset->id);
auto animClip = SHAssetManager::GetData<SHAnimClipAsset>(NEW_ASSET_ID);
animClip->name = newAssetName;
animClip->firstIndex = firstIndex;
animClip->lastIndex = lastIndex;
animClip->animRawDataAssetId = SHResourceManager::GetAssetID<SHRawAnimation>(rawAnimation).value_or(0);
SHAssetManager::SaveAsset(containerAsset->id);
// Close
isOpen = false;
if (onClose)
onClose(NEW_ASSET_ID);
ImGui::CloseCurrentPopup();
}
}
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
// Close
isOpen = false;
if (onClose)
onClose(INVALID_ASSET_ID);
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
/*-----------------------------------------------------------------------------------*/
/* Cosntructors/Destructors */
/*-----------------------------------------------------------------------------------*/
SHRawAnimInspector::SHRawAnimInspector()
: SHEditorWindow("Animation Editor", ImGuiWindowFlags_MenuBar)
{}
/*-----------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*-----------------------------------------------------------------------------------*/
void SHRawAnimInspector::Init()
{
SHEditorWindow::Init();
SHEditorWindowManager::CreatePopupWindow<SHAnimClipCreatePrompt>();
}
void SHRawAnimInspector::Update()
{
SHEditorWindow::Update();
// Draw
if (Begin())
{
// Ignore if no asset
if (container)
{
drawMenuBar();
// Button to add a new clip
if (ImGui::Button(std::format("{} Create Animation Clip", ICON_MD_ADD).data()))
{
auto prompt = SHEditorWindowManager::GetPopupWindow<SHAnimClipCreatePrompt>();
prompt->Init(containerAsset, container, currRawAnim, [this](AssetID createdAssetId)
{
if (createdAssetId != INVALID_ASSET_ID)
{
childAnimClips.emplace_back(SHResourceManager::LoadOrGet<SHAnimationClip>(createdAssetId));
}
});
prompt->isOpen = true;
}
// Render all animation clips
if (SHEditorUI::CollapsingHeader("Existing Animation Clips"))
{
ImGui::Indent();
for (auto animClip : childAnimClips)
{
bool changed = false;
std::optional<std::string> animClipName = SHResourceManager::GetAssetName<SHAnimationClip>(animClip);
int firstIndex = animClip->GetStartFrameIndex();
int endIndex = animClip->GetEndFrameIndex();
ImGui::Separator();
ImGui::Text(animClipName.has_value() ? animClipName.value().c_str() : "");
changed |= SHEditorWidgets::SliderInt
(
"Start", 0, currRawAnim->GetTotalFrames(),
[&]() { return firstIndex; },
[&](int i) { firstIndex = i; }
);
changed |= SHEditorWidgets::SliderInt
(
"End", 0, currRawAnim->GetTotalFrames(),
[&]() { return endIndex; },
[&](int i) { endIndex = i; }
);
// If there's a change we need to commit changes
if (changed && firstIndex < endIndex)
{
// Update runtime asset
*animClip = SHAnimationClip(currRawAnim, firstIndex, endIndex);
// Update serialized asset
auto assetId = SHResourceManager::GetAssetID(animClip);
if (assetId.has_value())
{
auto animAsset = SHAssetManager::GetData<SHAnimClipAsset>(assetId.value());
animAsset->firstIndex = firstIndex;
animAsset->lastIndex = endIndex;
SHAssetManager::SaveAsset(assetId.value());
}
}
}
// Extra separator if there is more than one
if (!childAnimClips.empty())
ImGui::Separator();
ImGui::Unindent();
}
}
else
{
SHEditorUI::CenteredText("Double click on a model file to inspect its animations here.");
}
}
ImGui::End();
}
void SHRawAnimInspector::Exit()
{
SHEditorWindow::Exit();
}
/*-----------------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------------*/
void SHRawAnimInspector::Open(AssetID assetId)
{
containerAsset = SHAssetManager::GetAsset(assetId);
container = SHAssetManager::GetData<SHAnimClipContainerAsset>(assetId);
// Load anim clips
if (container)
{
currRawAnim = SHResourceManager::LoadOrGet<SHRawAnimation>(container->animRawDataAssetId);
childAnimClips = getChildAnimClips(assetId);
}
else
{
childAnimClips.clear();
}
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
void SHRawAnimInspector::drawMenuBar()
{
if (ImGui::BeginMenuBar())
{
if (ImGui::Button(std::format("{} Save", ICON_MD_SAVE).data()))
{
}
const std::string& ASSET_NAME = SHResourceManager::GetAssetName<SHRawAnimation>(currRawAnim).value_or("Unnamed Asset");
ImGui::Text(ASSET_NAME.c_str());
ImGui::EndMenuBar();
}
}
std::vector<Handle<SHAnimationClip>> SHRawAnimInspector::getChildAnimClips(AssetID containerId)
{
auto const containerAsset {*SHAssetManager::GetAsset(containerId)};
std::vector<Handle<SHAnimationClip>> animClips;
for (auto const& asset : containerAsset.subAssets)
{
animClips.emplace_back(SHResourceManager::LoadOrGet<SHAnimationClip>(asset->id));
}
return animClips;
}
}

View File

@ -0,0 +1,110 @@
/************************************************************************************//*!
\file SHRawAnimInspector.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 2, 2023
\brief Contains the definition of SHRawAnimInspector.
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
// Project Includes
#include "Assets/SHAssetMacros.h"
#include "Editor/EditorWindow/SHEditorWindow.h"
#include "Resource/SHHandle.h"
#include "Animation/SHRawAnimation.h"
#include "Assets/SHAsset.h"
#include "Assets/Asset Types/SHAnimClipContainerAsset.h"
#include "Editor/EditorWindow/SHPopUpWindow.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Forward Declarations */
/*-----------------------------------------------------------------------------------*/
struct SHAnimClipAsset;
class SHRawAnimation;
class SHAnimationClip;
/*-----------------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------------*/
/// <summary>
/// Prompt for creating an animation clip. Init() must be called to pass in the correct
/// SHRawAnimation that the created clip will use.
/// </summary>
class SHAnimClipCreatePrompt : public SHPopUpWindow
{
public:
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*---------------------------------------------------------------------------------*/
SHAnimClipCreatePrompt();
/*---------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*---------------------------------------------------------------------------------*/
void Init(
SHAsset* contAsset,
SHAnimClipContainerAsset* cont,
Handle<SHRawAnimation> rawAnim,
std::function<void(AssetID)> onClose = nullptr
);
void Draw() override;
private:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
std::string newAssetName;
uint32_t firstIndex = 0;
uint32_t lastIndex = 0;
Handle<SHRawAnimation> rawAnimation;
SHAsset* containerAsset{nullptr};
SHAnimClipContainerAsset* container{nullptr};
std::function<void(AssetID)> onClose;
};
/// <summary>
/// Editor for generating SHAnimationClips from a single SHRawAnimation object.
/// </summary>
class SHRawAnimInspector final : public SHEditorWindow
{
public:
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*---------------------------------------------------------------------------------*/
SHRawAnimInspector();
~SHRawAnimInspector() = default;
/*---------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*---------------------------------------------------------------------------------*/
void Init() override;
void Update() override;
void Exit() override;
/*---------------------------------------------------------------------------------*/
/* Usage Functions */
/*---------------------------------------------------------------------------------*/
void Open(AssetID assetId);
private:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
SHAsset* containerAsset{nullptr};
SHAnimClipContainerAsset* container {nullptr};
Handle<SHRawAnimation> currRawAnim;
std::vector<Handle<SHAnimationClip>> childAnimClips;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
void drawMenuBar();
std::vector<Handle<SHAnimationClip>> getChildAnimClips(AssetID containerId);
};
}

View File

@ -8,3 +8,5 @@
#include "MaterialInspector/SHMaterialInspector.h" // Material Inspector #include "MaterialInspector/SHMaterialInspector.h" // Material Inspector
#include "ColliderTagPanel/SHColliderTagPanel.h" // Collider Tag Panel #include "ColliderTagPanel/SHColliderTagPanel.h" // Collider Tag Panel
#include "InputBindings/SHInputBindingsPanel.h" // Input Bindings #include "InputBindings/SHInputBindingsPanel.h" // Input Bindings
#include "EditorWindow/Animation/SHAnimationControllerEditor.h" // Animation Controller Editor
#include "EditorWindow/RawAnimationInspector/SHRawAnimInspector.h" // Raw Animation Inspector

View File

@ -39,6 +39,7 @@
//|| Library Includes || //|| Library Includes ||
//#==============================================================# //#==============================================================#
#include <imgui.h> #include <imgui.h>
#include <imnodes.h>
#include <SDL.h> #include <SDL.h>
#include <rttr/registration> #include <rttr/registration>
#include <ImGuizmo.h> #include <ImGuizmo.h>
@ -97,6 +98,10 @@ namespace SHADE
SHLOG_CRITICAL("Failed to create ImGui Context") SHLOG_CRITICAL("Failed to create ImGui Context")
} }
} }
if (ImNodes::CreateContext() == nullptr)
{
SHLOG_CRITICAL("Failed to create ImNodes Context")
}
#ifdef SHEDITOR #ifdef SHEDITOR
editorConfig = &SHConfigurationManager::LoadEditorConfig(); editorConfig = &SHConfigurationManager::LoadEditorConfig();
@ -113,6 +118,8 @@ namespace SHADE
SHEditorWindowManager::CreateEditorWindow<SHEditorInspector>(); SHEditorWindowManager::CreateEditorWindow<SHEditorInspector>();
SHEditorWindowManager::CreateEditorWindow<SHEditorViewport>(); SHEditorWindowManager::CreateEditorWindow<SHEditorViewport>();
SHEditorWindowManager::CreateEditorWindow<SHAnimationControllerEditor>();
SHEditorWindowManager::CreateEditorWindow<SHRawAnimInspector>();
//Add popup windows //Add popup windows
SHEditorWindowManager::CreatePopupWindow<SHSceneSavePrompt>(); SHEditorWindowManager::CreatePopupWindow<SHSceneSavePrompt>();
@ -340,6 +347,7 @@ namespace SHADE
{ {
window->Init(); window->Init();
} }
ImNodes::DestroyContext();
ImGui_ImplVulkan_Shutdown(); ImGui_ImplVulkan_Shutdown();
ImGui_ImplSDL2_Shutdown(); ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();

View File

@ -137,6 +137,15 @@ namespace SHADE
{ {
ImGui::Text(title.c_str()); ImGui::Text(title.c_str());
} }
void SHEditorUI::CenteredText(const std::string& text)
{
const auto WINDOW_SIZE = ImGui::GetWindowSize();
const auto TEXT_SIZE = ImGui::CalcTextSize(text.c_str());
ImGui::SetCursorPosX((WINDOW_SIZE.x - TEXT_SIZE.x) * 0.5f);
ImGui::SetCursorPosY((WINDOW_SIZE.y - TEXT_SIZE.y) * 0.5f);
ImGui::Text(text.c_str());
}
bool SHEditorUI::SmallButton(const std::string& title) bool SHEditorUI::SmallButton(const std::string& title)
{ {
return ImGui::SmallButton(title.c_str()); return ImGui::SmallButton(title.c_str());
@ -369,9 +378,9 @@ namespace SHADE
// Attempt to get the asset's data for rendering editor // Attempt to get the asset's data for rendering editor
auto asset = SHAssetManager::GetAsset(value); auto asset = SHAssetManager::GetAsset(value);
std::string assetName; std::string assetName;
if (asset.has_value()) if (asset)
{ {
assetName = asset.value().name; assetName = asset->name;
} }
// Editor // Editor
@ -382,9 +391,9 @@ namespace SHADE
{ {
// Check if type matches // Check if type matches
auto draggedAsset = SHAssetManager::GetAsset(*payload); auto draggedAsset = SHAssetManager::GetAsset(*payload);
if (draggedAsset.has_value() && draggedAsset.value().type == type) if (draggedAsset && draggedAsset->type == type)
{ {
value = draggedAsset.value().id; value = draggedAsset->id;
changed = true; changed = true;
} }
SHDragDrop::EndTarget(); SHDragDrop::EndTarget();

View File

@ -150,6 +150,12 @@ namespace SHADE
/// <param name="title">Text to display.</param> /// <param name="title">Text to display.</param>
static void Text(const std::string& title); static void Text(const std::string& title);
/// <summary> /// <summary>
/// Renders a text widget that is vertically and horizontally centered in the current
/// window.
/// </summary>
/// <param name="text">Text to display.</param>
static void CenteredText(const std::string& text);
/// <summary>
/// Creates a small inline button widget. /// Creates a small inline button widget.
/// <br/> /// <br/>
/// Wraps up ImGui::SmallButton(). /// Wraps up ImGui::SmallButton().

View File

@ -418,12 +418,16 @@ namespace SHADE
} }
template<typename T> template<typename T>
static bool DragDropReadOnlyField(std::string const& label, std::string_view const& fieldVTextValue, std::function<T (void)> const& get, std::function<void(T const&)> const& set, SHDragDrop::DragDropTag const& dragDropTag, std::string_view const& tooltip = {}) static bool DragDropReadOnlyField(std::string const& label, std::string_view const& fieldVTextValue, std::function<T (void)> const& get, std::function<void(T const&)> const& set, SHDragDrop::DragDropTag const& dragDropTag, std::string_view const& tooltip = {}, float customWidth = -1.0f)
{ {
std::string text = fieldVTextValue.data(); std::string text = fieldVTextValue.data();
ImGui::BeginGroup(); ImGui::BeginGroup();
ImGui::PushID(label.data()); ImGui::PushID(label.data());
TextLabel(label); TextLabel(label);
if (customWidth > 0.0f)
{
ImGui::SetNextItemWidth(customWidth);
}
bool changed = ImGui::InputText("##inputText", &text, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_AutoSelectAll, nullptr, nullptr); bool changed = ImGui::InputText("##inputText", &text, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_AutoSelectAll, nullptr, nullptr);
if(SHDragDrop::BeginTarget()) if(SHDragDrop::BeginTarget())
{ {

View File

@ -127,8 +127,8 @@ namespace SHADE
if (assetId.has_value()) if (assetId.has_value())
{ {
const auto ASSET_INFO = SHAssetManager::GetAsset(assetId.value()); const auto ASSET_INFO = SHAssetManager::GetAsset(assetId.value());
if (ASSET_INFO.has_value()) if (ASSET_INFO)
return ASSET_INFO.value().name; return ASSET_INFO->name;
} }
return {}; return {};
} }

View File

@ -21,6 +21,8 @@ of DigiPen Institute of Technology is prohibited.
#include "Assets/Asset Types/Models/SHModelAsset.h" #include "Assets/Asset Types/Models/SHModelAsset.h"
#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/SHAnimClipContainerAsset.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"
@ -28,7 +30,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h"
#include "Assets/Asset Types/SHMaterialAsset.h" #include "Assets/Asset Types/SHMaterialAsset.h"
#include "Graphics/MiddleEnd/TextRendering/SHFont.h" #include "Graphics/MiddleEnd/TextRendering/SHFont.h"
#include "Animation/SHAnimationClip.h" #include "Animation/SHRawAnimation.h"
#include "Animation/SHRig.h" #include "Animation/SHRig.h"
namespace SHADE namespace SHADE
@ -38,6 +40,8 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
class SHMaterial; class SHMaterial;
struct SHRigNode; struct SHRigNode;
class SHAnimationClip;
class SHAnimationController;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
@ -53,8 +57,10 @@ namespace SHADE
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<SHAnimationClip> { 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<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

@ -25,6 +25,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h"
#include "Serialization/SHYAMLConverters.h" #include "Serialization/SHYAMLConverters.h"
#include "Animation/SHAnimationClip.h"
namespace SHADE namespace SHADE
{ {
@ -41,8 +42,10 @@ namespace SHADE
!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, SHRig> &&
!std::is_same_v<ResourceType, SHAnimationClip> && !std::is_same_v<ResourceType, SHAnimationClip> &&
!std::is_same_v<ResourceType, SHRig> !std::is_same_v<ResourceType, SHAnimationController>
) )
{ {
static_assert(true, "Unsupported Resource Type specified for SHResourceManager."); static_assert(true, "Unsupported Resource Type specified for SHResourceManager.");
@ -159,8 +162,8 @@ namespace SHADE
if (assetId.has_value()) if (assetId.has_value())
{ {
const auto ASSET_INFO = SHAssetManager::GetAsset(assetId.value()); const auto ASSET_INFO = SHAssetManager::GetAsset(assetId.value());
if (ASSET_INFO.has_value()) if (ASSET_INFO)
return ASSET_INFO.value().name; return ASSET_INFO->name;
} }
return {}; return {};
} }
@ -355,10 +358,30 @@ namespace SHADE
loadedAssetData.emplace_back(assetId); loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType>(assetData.rig, rigNodeStore); return resourceHub.Create<ResourceType>(assetData.rig, rigNodeStore);
} }
else if constexpr (std::is_same_v<ResourceType, SHRawAnimation>)
{
loadedAssetData.emplace_back(assetId);
if (assetData.anims.empty())
return {};
return resourceHub.Create<ResourceType>(*assetData.anims[0]);
}
else if constexpr (std::is_same_v<ResourceType, SHAnimationClip>) else if constexpr (std::is_same_v<ResourceType, SHAnimationClip>)
{ {
loadedAssetData.emplace_back(assetId); loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType>(*assetData.anims[0]); return resourceHub.Create<ResourceType>
(
LoadOrGet<SHRawAnimation>(assetData.animRawDataAssetId),
assetData.firstIndex,
assetData.lastIndex
);
}
else if constexpr (std::is_same_v<ResourceType, SHAnimationController>)
{
loadedAssetData.emplace_back(assetId);
return resourceHub.Create<ResourceType>
(
// TODO
);
} }
} }
} }

View File

@ -44,6 +44,21 @@ namespace SHADE
return SHResourceManager::LoadOrGet<SHFont>(assetId); return SHResourceManager::LoadOrGet<SHFont>(assetId);
} }
Handle<SHAnimationClip> SHResourceManagerInterface::LoadOrGetAnimationClip(AssetID assetId)
{
return SHResourceManager::LoadOrGet<SHAnimationClip>(assetId);
}
Handle<SHAnimationController> SHResourceManagerInterface::LoadOrGetAnimationController(AssetID assetId)
{
return SHResourceManager::LoadOrGet<SHAnimationController>(assetId);
}
Handle<SHRig> SHResourceManagerInterface::LoadOrGetRig(AssetID assetId)
{
return SHResourceManager::LoadOrGet<SHRig>(assetId);
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Query Functions */ /* Query Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/

View File

@ -29,6 +29,9 @@ namespace SHADE
struct SHMaterialSpec; struct SHMaterialSpec;
class SHMaterial; class SHMaterial;
class SHFont; class SHFont;
class SHAnimationClip;
class SHRig;
class SHAnimationController;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
@ -80,6 +83,24 @@ namespace SHADE
/// <param name="assetId">Asset ID of the resource to load.</param> /// <param name="assetId">Asset ID of the resource to load.</param>
/// <returns>Handle to the resource to retrieve.</returns> /// <returns>Handle to the resource to retrieve.</returns>
static Handle<SHFont> LoadOrGetFont(AssetID assetId); static Handle<SHFont> LoadOrGetFont(AssetID assetId);
/// <summary>
/// Wrapper for SHResourceManager::LoadOrGet<SHAnimationClip>().
/// </summary>
/// <param name="assetId">Asset ID of the resource to load.</param>
/// <returns>Handle to the resource to retrieve.</returns>
static Handle<SHAnimationClip> LoadOrGetAnimationClip(AssetID assetId);
/// <summary>
/// Wrapper for SHResourceManager::LoadOrGet<SHAnimationController>().
/// </summary>
/// <param name="assetId">Asset ID of the resource to load.</param>
/// <returns>Handle to the resource to retrieve.</returns>
static Handle<SHAnimationController> LoadOrGetAnimationController(AssetID assetId);
/// <summary>
/// Wrapper for SHResourceManager::LoadOrGet<SHRig>().
/// </summary>
/// <param name="assetId">Asset ID of the resource to load.</param>
/// <returns>Handle to the resource to retrieve.</returns>
static Handle<SHRig> LoadOrGetRig(AssetID assetId);
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Query Functions */ /* Query Functions */

View File

@ -115,7 +115,7 @@ namespace SHADE
#else #else
static constexpr int EXCESS_CHARS_COUNT = 2; static constexpr int EXCESS_CHARS_COUNT = 2;
const auto RESULT = SHExecUtilties::ExecBlockingPowerShellCommand(L"./vswhere -version \"[15.0,19.0]\" -requires Microsoft.NetCore.Component.DevelopmentTools -find Common7\\\\IDE\\\\devenv.exe | Select-Object -first 1", true, true); const auto RESULT = SHExecUtilties::ExecBlockingPowerShellCommand(L"./vswhere -version \"[15.0,21.0]\" -requires Microsoft.NetCore.Component.DevelopmentTools -find Common7\\\\IDE\\\\devenv.exe | Select-Object -last 1", true, true);
if (RESULT.StdOutput.size() < EXCESS_CHARS_COUNT) if (RESULT.StdOutput.size() < EXCESS_CHARS_COUNT)
{ {
SHLOG_ERROR("[SHVSUtilities] Unable to get path to Visual Studio installation. SHVSUtilities will not work."); SHLOG_ERROR("[SHVSUtilities] Unable to get path to Visual Studio installation. SHVSUtilities will not work.");

View File

@ -428,13 +428,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)
@ -443,9 +443,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;
} }

View File

@ -0,0 +1,70 @@
/************************************************************************************//*!
\file AnimationClipAsset.cxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the implementation of the functions of the managed
AnimationClip class.
Note: This file is written in C++17/CLI.
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.
*//*************************************************************************************/
// Precompiled Headers
#include "SHpch.h"
// Primary Header
#include "AnimationClipAsset.hxx"
// External Dependencies
#include "Resource/SHResourceManagerInterface.h"
// Project Headers
#include "Utility/Convert.hxx"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Properties */
/*---------------------------------------------------------------------------------*/
Handle<SHAnimationClip> AnimationClipAsset::NativeObject::get()
try
{
return SHResourceManagerInterface::LoadOrGetAnimationClip(asset.NativeAssetID);
}
catch (const BadHandleCastException&)
{
return Handle<SHAnimationClip>();
}
AssetID AnimationClipAsset::NativeAssetID::get()
{
return asset.NativeAssetID;
}
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructor */
/*---------------------------------------------------------------------------------*/
AnimationClipAsset::AnimationClipAsset(AssetID AnimationClipId)
: asset{ AnimationClipId }
{}
/*---------------------------------------------------------------------------------*/
/* Operator Overloads */
/*---------------------------------------------------------------------------------*/
AnimationClipAsset::operator bool(AnimationClipAsset asset)
{
return asset.asset;
}
/*---------------------------------------------------------------------------------*/
/* Conversion Operators */
/*---------------------------------------------------------------------------------*/
AnimationClipAsset::operator Asset(AnimationClipAsset nativeAsset)
{
return nativeAsset.asset;
}
AnimationClipAsset::operator AnimationClipAsset(Asset asset)
{
return AnimationClipAsset(asset.NativeAssetID);
}
}

View File

@ -0,0 +1,90 @@
/************************************************************************************//*!
\file AnimationClipAsset.hxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the definition of the managed AnimationClipAsset class.
Note: This file is written in C++17/CLI.
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
// External Dependencies
#include "Resource/SHHandle.h"
#include "Animation/SHAnimationClip.h"
// Project Includes
#include "NativeAsset.hxx"
#include "Engine/GenericHandle.hxx"
namespace SHADE
{
/// <summary>
/// Managed counterpart of the native Animation Clip object that specifies a range of
/// animation frames that can be specified to an Animator component to play an
/// animation.
/// </summary>
public value struct AnimationClipAsset
{
internal:
/*-----------------------------------------------------------------------------*/
/* Properties */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Copy of the Handle to the native object.
/// </summary>
property Handle<SHAnimationClip> NativeObject
{
Handle<SHAnimationClip> get();
}
/// <summary>
/// The raw asset ID of the asset.
/// </summary>
property AssetID NativeAssetID
{
AssetID get();
}
/*-----------------------------------------------------------------------------*/
/* Constructors/Destructor */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Constructor for the AnimationClip.
/// </summary>
/// <param name="AnimationClipId">AssetID to the AnimationClip asset.</param>
AnimationClipAsset(AssetID AnimationClipId);
/*-----------------------------------------------------------------------------*/
/* Operator Overloads */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Implicit conversion operator to enable checking if a AnimationClip is valid.
/// </summary>
/// <param name="gameObj">Asset to check.</param>
/// <returns>True if the Asset is valid.</returns>
static operator bool(AnimationClipAsset asset);
/*-----------------------------------------------------------------------------*/
/* Conversion Operators */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Conversion operator to enable casting from a AnimationClip to an Asset.
/// </summary>
/// <param name="vec">Vector3 to convert from.</param>
static explicit operator Asset(AnimationClipAsset nativeAsset);
/// <summary>
/// Conversion operator to enable casting from a Asset to a AnimationClip.
/// </summary>
/// <param name="asset"></param>
static explicit operator AnimationClipAsset(Asset asset);
protected:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
Asset asset;
};
}

View File

@ -0,0 +1,70 @@
/************************************************************************************//*!
\file AnimationController.cxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the implementation of the functions of the managed
AnimationController class.
Note: This file is written in C++17/CLI.
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.
*//*************************************************************************************/
// Precompiled Headers
#include "SHpch.h"
// Primary Header
#include "AnimationControllerAsset.hxx"
// External Dependencies
#include "Resource/SHResourceManagerInterface.h"
// Project Headers
#include "Utility/Convert.hxx"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Properties */
/*---------------------------------------------------------------------------------*/
Handle<SHAnimationController> AnimationControllerAsset::NativeObject::get()
try
{
return SHResourceManagerInterface::LoadOrGetAnimationController(asset.NativeAssetID);
}
catch (const BadHandleCastException&)
{
return Handle<SHAnimationController>();
}
AssetID AnimationControllerAsset::NativeAssetID::get()
{
return asset.NativeAssetID;
}
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructor */
/*---------------------------------------------------------------------------------*/
AnimationControllerAsset::AnimationControllerAsset(AssetID AnimationControllerId)
: asset{ AnimationControllerId }
{}
/*---------------------------------------------------------------------------------*/
/* Operator Overloads */
/*---------------------------------------------------------------------------------*/
AnimationControllerAsset::operator bool(AnimationControllerAsset asset)
{
return asset.asset;
}
/*---------------------------------------------------------------------------------*/
/* Conversion Operators */
/*---------------------------------------------------------------------------------*/
AnimationControllerAsset::operator Asset(AnimationControllerAsset nativeAsset)
{
return nativeAsset.asset;
}
AnimationControllerAsset::operator AnimationControllerAsset(Asset asset)
{
return AnimationControllerAsset(asset.NativeAssetID);
}
}

View File

@ -0,0 +1,89 @@
/************************************************************************************//*!
\file AnimationControllerAsset.hxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the definition of the managed AnimationController class.
Note: This file is written in C++17/CLI.
Copyright (C) 2022 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
// External Dependencies
#include "Resource/SHHandle.h"
#include "Animation/SHAnimationController.h"
// Project Includes
#include "NativeAsset.hxx"
#include "Engine/GenericHandle.hxx"
namespace SHADE
{
/// <summary>
/// Managed counterpart of the native AnimationController object containing the
/// state machine for controlling what AnimationClips that an Animator should play.
/// </summary>
public value struct AnimationControllerAsset
{
internal:
/*-----------------------------------------------------------------------------*/
/* Properties */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Copy of the Handle to the native object.
/// </summary>
property Handle<SHAnimationController> NativeObject
{
Handle<SHAnimationController> get();
}
/// <summary>
/// The raw asset ID of the asset.
/// </summary>
property AssetID NativeAssetID
{
AssetID get();
}
/*-----------------------------------------------------------------------------*/
/* Constructors/Destructor */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Constructor for the AnimationController.
/// </summary>
/// <param name="AnimationControllerId">AssetID to the AnimationController asset.</param>
AnimationControllerAsset(AssetID AnimationControllerId);
/*-----------------------------------------------------------------------------*/
/* Operator Overloads */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Implicit conversion operator to enable checking if a AnimationController is valid.
/// </summary>
/// <param name="gameObj">Asset to check.</param>
/// <returns>True if the Asset is valid.</returns>
static operator bool(AnimationControllerAsset asset);
/*-----------------------------------------------------------------------------*/
/* Conversion Operators */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Conversion operator to enable casting from a AnimationController to an Asset.
/// </summary>
/// <param name="vec">Vector3 to convert from.</param>
static explicit operator Asset(AnimationControllerAsset nativeAsset);
/// <summary>
/// Conversion operator to enable casting from a Asset to a AnimationController.
/// </summary>
/// <param name="asset"></param>
static explicit operator AnimationControllerAsset(Asset asset);
protected:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
Asset asset;
};
}

View File

@ -0,0 +1,70 @@
/************************************************************************************//*!
\file AnimationRigAsset.cxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the implementation of the functions of the managed
AnimationRig class.
Note: This file is written in C++17/CLI.
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.
*//*************************************************************************************/
// Precompiled Headers
#include "SHpch.h"
// Primary Header
#include "AnimationRigAsset.hxx"
// External Dependencies
#include "Resource/SHResourceManagerInterface.h"
// Project Headers
#include "Utility/Convert.hxx"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Properties */
/*---------------------------------------------------------------------------------*/
Handle<SHRig> AnimationRigAsset::NativeObject::get()
try
{
return SHResourceManagerInterface::LoadOrGetRig(asset.NativeAssetID);
}
catch (const BadHandleCastException&)
{
return Handle<SHRig>();
}
AssetID AnimationRigAsset::NativeAssetID::get()
{
return asset.NativeAssetID;
}
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructor */
/*---------------------------------------------------------------------------------*/
AnimationRigAsset::AnimationRigAsset(AssetID AnimationRigId)
: asset{ AnimationRigId }
{}
/*---------------------------------------------------------------------------------*/
/* Operator Overloads */
/*---------------------------------------------------------------------------------*/
AnimationRigAsset::operator bool(AnimationRigAsset asset)
{
return asset.asset;
}
/*---------------------------------------------------------------------------------*/
/* Conversion Operators */
/*---------------------------------------------------------------------------------*/
AnimationRigAsset::operator Asset(AnimationRigAsset nativeAsset)
{
return nativeAsset.asset;
}
AnimationRigAsset::operator AnimationRigAsset(Asset asset)
{
return AnimationRigAsset(asset.NativeAssetID);
}
}

View File

@ -0,0 +1,89 @@
/************************************************************************************//*!
\file AnimationRigAsset.hxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the definition of the managed AnimationRigAsset class.
Note: This file is written in C++17/CLI.
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
// External Dependencies
#include "Resource/SHHandle.h"
#include "Animation/SHRig.h"
// Project Includes
#include "NativeAsset.hxx"
#include "Engine/GenericHandle.hxx"
namespace SHADE
{
/// <summary>
/// Managed counterpart of the native Animation Rig object that specifies how an
/// Animation Clip affects the model that this Rig is attached to.
/// </summary>
public value struct AnimationRigAsset
{
internal:
/*-----------------------------------------------------------------------------*/
/* Properties */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Copy of the Handle to the native object.
/// </summary>
property Handle<SHRig> NativeObject
{
Handle<SHRig> get();
}
/// <summary>
/// The raw asset ID of the asset.
/// </summary>
property AssetID NativeAssetID
{
AssetID get();
}
/*-----------------------------------------------------------------------------*/
/* Constructors/Destructor */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Constructor for the AnimationRig.
/// </summary>
/// <param name="AnimationRigId">AssetID to the AnimationRig asset.</param>
AnimationRigAsset(AssetID AnimationRigId);
/*-----------------------------------------------------------------------------*/
/* Operator Overloads */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Implicit conversion operator to enable checking if a AnimationRig is valid.
/// </summary>
/// <param name="gameObj">Asset to check.</param>
/// <returns>True if the Asset is valid.</returns>
static operator bool(AnimationRigAsset asset);
/*-----------------------------------------------------------------------------*/
/* Conversion Operators */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Conversion operator to enable casting from a AnimationRig to an Asset.
/// </summary>
/// <param name="vec">Vector3 to convert from.</param>
static explicit operator Asset(AnimationRigAsset nativeAsset);
/// <summary>
/// Conversion operator to enable casting from a Asset to a AnimationRig.
/// </summary>
/// <param name="asset"></param>
static explicit operator AnimationRigAsset(Asset asset);
protected:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
Asset asset;
};
}

View File

@ -0,0 +1,159 @@
/************************************************************************************//*!
\file Animator.cxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the definition of the functions of the managed Animator class.
Note: This file is written in C++17/CLI.
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.
*//*************************************************************************************/
// Precompiled Headers
#include "SHpch.h"
// Primary Header
#include "Animator.hxx"
#include "Assets/NativeAsset.hxx"
#include "Utility/Convert.hxx"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Constructors */
/*---------------------------------------------------------------------------------*/
Animator::Animator(Entity entity)
: Component(entity)
{}
/*---------------------------------------------------------------------------------*/
/* Properties */
/*---------------------------------------------------------------------------------*/
AnimationControllerAsset Animator::AnimationController::get()
{
auto controller = GetNativeComponent()->GetAnimationController();
return controller ? AnimationControllerAsset(controller) : AnimationControllerAsset();
}
void Animator::AnimationController::set(AnimationControllerAsset value)
{
if (value)
{
GetNativeComponent()->SetAnimationController(Handle<SHAnimationController>());
}
else
{
GetNativeComponent()->SetAnimationController(value.NativeObject);
}
}
AnimationRigAsset Animator::Rig::get()
{
auto rig = GetNativeComponent()->GetRig();
return rig ? AnimationRigAsset(rig) : AnimationRigAsset();
}
void Animator::Rig::set(AnimationRigAsset value)
{
if (value)
{
GetNativeComponent()->SetRig(Handle<SHRig>());
}
else
{
GetNativeComponent()->SetRig(Handle<SHRig>(value.NativeObject));
}
}
System::String^ Animator::CurrentNodeName::get()
{
const auto CURR_NODE = GetNativeComponent()->GetCurrentNode();
if (CURR_NODE)
return Convert::ToCLI(CURR_NODE->Name);
return nullptr;
}
/*---------------------------------------------------------------------------------*/
/* Usage Functions */
/*---------------------------------------------------------------------------------*/
void Animator::Play()
{
GetNativeComponent()->Play();
}
void Animator::Play(AnimationClipAsset clip)
{
GetNativeComponent()->Play(clip.NativeObject);
}
void Animator::PlayOneShot(AnimationClipAsset clip)
{
GetNativeComponent()->PlayOneShot(clip.NativeObject);
}
void Animator::PlayFromStart()
{
GetNativeComponent()->Play();
}
void Animator::Pause()
{
GetNativeComponent()->Pause();
}
void Animator::Stop()
{
GetNativeComponent()->Stop();
}
/*---------------------------------------------------------------------------------*/
/* Parameter Functions */
/*---------------------------------------------------------------------------------*/
generic<typename T>
void Animator::SetParameter(System::String^ paramName, T value)
{
if (T::typeid == int::typeid)
{
GetNativeComponent()->SetParameter<int>(Convert::ToNative(paramName), static_cast<int>(value));
}
else if (T::typeid == float::typeid)
{
GetNativeComponent()->SetParameter<float>(Convert::ToNative(paramName), static_cast<float>(value));
}
else if (T::typeid == bool::typeid)
{
GetNativeComponent()->SetParameter<bool>(Convert::ToNative(paramName), static_cast<bool>(value));
}
}
void Animator::SetTrigger(System::String^ paramName)
{
GetNativeComponent()->SetTrigger(Convert::ToNative(paramName));
}
System::Nullable<int> Animator::GetIntParameter(System::String^ paramName)
{
auto val = GetNativeComponent()->GetParameter<int>(Convert::ToNative(paramName));
if (val.has_value())
return System::Nullable<int>(val.value());
return {};
}
System::Nullable<float> Animator::GetFloatParameter(System::String^ paramName)
{
auto val = GetNativeComponent()->GetParameter<float>(Convert::ToNative(paramName));
if (val.has_value())
return System::Nullable<float>(val.value());
return {};
}
System::Nullable<bool> Animator::GetBoolParameter(System::String^ paramName)
{
auto val = GetNativeComponent()->GetParameter<bool>(Convert::ToNative(paramName));
if (val.has_value())
return System::Nullable<bool>(val.value());
return {};
}
System::Nullable<bool> Animator::GetTriggerState(System::String^ paramName)
{
return GetBoolParameter(paramName);
}
}

View File

@ -0,0 +1,164 @@
/************************************************************************************//*!
\file Animator.hxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Mar 8, 2023
\brief Contains the definition of the managed Animator class with the
declaration of functions for working with it.
Note: This file is written in C++17/CLI.
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
// Project Includes
#include "Components/Component.hxx"
// External Dependencies
#include "Animation/SHAnimatorComponent.h"
// Project Includes
#include "Assets/AnimationClipAsset.hxx"
#include "Assets/AnimationControllerAsset.hxx"
#include "Assets/AnimationRigAsset.hxx"
namespace SHADE
{
/// <summary>
/// CLR version of the SHADE Engine's SHAnimatorComponent.
/// </summary>
public ref class Animator : public Component<SHAnimatorComponent>
{
internal:
/*-----------------------------------------------------------------------------*/
/* Constructors */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Constructs a Animator Component that represents a native Animator
/// component tied to the specified Entity.
/// </summary>
/// <param name="entity">Entity that this Component will be tied to.</param>
Animator(Entity entity);
public:
/*-----------------------------------------------------------------------------*/
/* Properties */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Animation Controller used to controller the animation of this Animator.
/// </summary>
property AnimationControllerAsset AnimationController
{
AnimationControllerAsset get();
void set(AnimationControllerAsset value);
}
/// <summary>
/// The shared Material used to render this Animator and other Animators
/// using the same base Material.
/// </summary>
property AnimationRigAsset Rig
{
AnimationRigAsset get();
void set(AnimationRigAsset value);
}
/// <summary>
/// Name of the current node if there is an animation controller attached. If
/// there is none, null is returned.
/// </summary>
property System::String^ CurrentNodeName
{
System::String^ get();
}
/*-----------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Plays the currently loaded animation from the last time.
/// </summary>
void Play();
/// <summary>
/// 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(AnimationClipAsset clip);
/// <summary>
/// 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(AnimationClipAsset clip);
/// <summary>
/// Plays the currently loaded animation clip from the start. Note that this only
/// works when using manual playback mode.
/// </summary>
void PlayFromStart();
/// <summary>
/// Pauses the animation at the current time.
/// </summary>
void Pause();
/// <summary>
/// Stops the animation and resets the play time back to 0. Note that this only
/// works when using manual playback mode. This is not supported when using an
/// Animation Controller.
/// </summary>
void Stop();
/*-----------------------------------------------------------------------------*/
/* Parameter Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Sets the parameter of the for the string. Does nothing if an invalid param name
/// is provided. Type of the parameter is not checked. Also does nothing if no
/// animation controller is specified.
/// </summary>
/// <typeparam name="T">
/// Type of parameter. Only bool, int, floats are supported.
/// </typeparam>
/// <param name="paramName">Name of the parameter.</param>
/// <param name="value">Value to set the parameter to.</param>
generic<typename T>
void SetParameter(System::String^ paramName, T value);
/// <summary>
/// Sets the flag for a trigger parameter. Does nothing if an invalid param name is
/// provided or if the param name refers to a parameter that is not a trigger.
/// </summary>
/// <param name="paramName">Name of the parameter.</param>
void SetTrigger(System::String^ paramName);
/// <summary>
/// Gets the parameter of the for the named parameter of type int. Types are
/// checked and will not return a value if there is nothing. Returns nothing if
/// there is no animation controller specified either.
/// </summary>
/// <param name="paramName">Name of the parameter.</param>
/// <returns>The value of the parameter or nothing if invalid.</returns>
System::Nullable<int> GetIntParameter(System::String^ paramName);
/// <summary>
/// Gets the parameter of the for the named parameter of type float. Types are
/// checked and will not return a value if there is nothing. Returns nothing if
/// there is no animation controller specified either.
/// </summary>
/// <param name="paramName">Name of the parameter.</param>
/// <returns>The value of the parameter or nothing if invalid.</returns>
System::Nullable<float> GetFloatParameter(System::String^ paramName);
/// <summary>
/// Gets the parameter of the for the named parameter of type bool. Types are
/// checked and will not return a value if there is nothing. Returns nothing if
/// there is no animation controller specified either.
/// </summary>
/// <param name="paramName">Name of the parameter.</param>
/// <returns>The value of the parameter or nothing if invalid.</returns>
System::Nullable<bool> GetBoolParameter(System::String^ paramName);
/// <summary>
/// Checks if the trigger flag for the named trigger parameter is set. Types are
/// checked and will not return a value if there is nothing. Returns nothing if
/// there is no animation controller specified either.
/// </summary>
/// <param name="paramName">Name of the parameter.</param>
/// <returns>True if the trigger is set.</returns>
System::Nullable<bool> GetTriggerState(System::String^ paramName);
};
}

View File

@ -18,6 +18,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Editor/Editor.hxx" #include "Editor/Editor.hxx"
// STL Includes // STL Includes
#include <memory> #include <memory>
#include <string>
// Project Headers // Project Headers
#include "Components/Component.hxx" #include "Components/Component.hxx"
#include "Scripts/ScriptStore.hxx" #include "Scripts/ScriptStore.hxx"
@ -30,7 +31,9 @@ of DigiPen Institute of Technology is prohibited.
#include "RangeAttribute.hxx" #include "RangeAttribute.hxx"
#include "Math/Vector2.hxx" #include "Math/Vector2.hxx"
#include "Math/Vector3.hxx" #include "Math/Vector3.hxx"
#include <string> #include "Assets/AnimationClipAsset.hxx"
#include "Assets/AnimationControllerAsset.hxx"
#include "Assets/AnimationRigAsset.hxx"
// Using Directives // Using Directives
using namespace System; using namespace System;
@ -180,7 +183,10 @@ namespace SHADE
renderSpecificField<int , System::Enum >(field, object, nullptr , &isHovered) || renderSpecificField<int , System::Enum >(field, object, nullptr , &isHovered) ||
renderSpecificField<AssetID , FontAsset >(field, object, nullptr , &isHovered) || renderSpecificField<AssetID , FontAsset >(field, object, nullptr , &isHovered) ||
renderSpecificField<AssetID , MeshAsset >(field, object, nullptr , &isHovered) || renderSpecificField<AssetID , MeshAsset >(field, object, nullptr , &isHovered) ||
renderSpecificField<AssetID , MaterialAsset >(field, object, nullptr , &isHovered); renderSpecificField<AssetID , MaterialAsset >(field, object, nullptr , &isHovered) ||
renderSpecificField<AssetID , AnimationClipAsset >(field, object, nullptr , &isHovered) ||
renderSpecificField<AssetID , AnimationControllerAsset>(field, object, nullptr , &isHovered) ||
renderSpecificField<AssetID , AnimationRigAsset >(field, object, nullptr , &isHovered);
if (!MODIFIED_PRIMITIVE) if (!MODIFIED_PRIMITIVE)
{ {
@ -341,7 +347,10 @@ namespace SHADE
renderFieldEditor<int , System::Enum >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || renderFieldEditor<int , System::Enum >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<AssetID , FontAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || renderFieldEditor<AssetID , FontAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<AssetID , MeshAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || renderFieldEditor<AssetID , MeshAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<AssetID , MaterialAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified); renderFieldEditor<AssetID , MaterialAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<AssetID , AnimationClipAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<AssetID , AnimationControllerAsset>(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<AssetID , AnimationRigAsset >(fieldName, object, nullptr , nullptr, rangeAttrib, modified);
return modified; return modified;
} }

View File

@ -23,6 +23,9 @@ of DigiPen Institute of Technology is prohibited.
#include "Assets/FontAsset.hxx" #include "Assets/FontAsset.hxx"
#include "Assets/MeshAsset.hxx" #include "Assets/MeshAsset.hxx"
#include "Assets/MaterialAsset.hxx" #include "Assets/MaterialAsset.hxx"
#include "Assets/AnimationClipAsset.hxx"
#include "Assets/AnimationControllerAsset.hxx"
#include "Assets/AnimationRigAsset.hxx"
namespace SHADE namespace SHADE
{ {
@ -237,6 +240,42 @@ namespace SHADE
return true; return true;
} }
return false;
}
template<>
bool Editor::renderFieldEditorInternal<AssetID, AnimationClipAsset>(const std::string& fieldName, interior_ptr<AnimationClipAsset> managedValPtr, EditorFieldFunc<uint32_t>, bool* isHovered, RangeAttribute^)
{
uint32_t assetId = managedValPtr->NativeAssetID;
if (SHEditorUI::InputAssetField(fieldName, assetId, AssetType::ANIM_CLIP, isHovered))
{
*managedValPtr = AnimationClipAsset(assetId);
return true;
}
return false;
}
template<>
bool Editor::renderFieldEditorInternal<AssetID, AnimationControllerAsset>(const std::string& fieldName, interior_ptr<AnimationControllerAsset> managedValPtr, EditorFieldFunc<uint32_t>, bool* isHovered, RangeAttribute^)
{
uint32_t assetId = managedValPtr->NativeAssetID;
if (SHEditorUI::InputAssetField(fieldName, assetId, AssetType::ANIM_CONTROLLER, isHovered))
{
*managedValPtr = AnimationControllerAsset(assetId);
return true;
}
return false;
}
template<>
bool Editor::renderFieldEditorInternal<AssetID, AnimationRigAsset>(const std::string& fieldName, interior_ptr<AnimationRigAsset> managedValPtr, EditorFieldFunc<uint32_t>, bool* isHovered, RangeAttribute^)
{
uint32_t assetId = managedValPtr->NativeAssetID;
if (SHEditorUI::InputAssetField(fieldName, assetId, AssetType::MODEL, isHovered))
{
*managedValPtr = AnimationRigAsset(assetId);
return true;
}
return false; return false;
} }
} }

View File

@ -32,6 +32,8 @@ of DigiPen Institute of Technology is prohibited.
#include "UI\SHUIComponent.h" #include "UI\SHUIComponent.h"
#include "UI\SHSliderComponent.h" #include "UI\SHSliderComponent.h"
#include "UI\SHCanvasComponent.h" #include "UI\SHCanvasComponent.h"
#include "Animation\SHAnimatorComponent.h"
#include "Graphics\MiddleEnd\TrajectoryRendering\SHTrajectoryRenderableComponent.h"
// Project Headers // Project Headers
#include "Utility/Convert.hxx" #include "Utility/Convert.hxx"
#include "Utility/Debug.hxx" #include "Utility/Debug.hxx"
@ -47,7 +49,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Components\Canvas.hxx" #include "Components\Canvas.hxx"
#include "Components\Slider.hxx" #include "Components\Slider.hxx"
#include "Components\TrajectoryRenderable.hxx" #include "Components\TrajectoryRenderable.hxx"
#include "Graphics\MiddleEnd\TrajectoryRendering\SHTrajectoryRenderableComponent.h" #include "Components\Animator.hxx"
@ -338,6 +340,7 @@ namespace SHADE
componentMap.Add(createComponentSet<SHCanvasComponent, Canvas>()); componentMap.Add(createComponentSet<SHCanvasComponent, Canvas>());
componentMap.Add(createComponentSet<SHSliderComponent, Slider>()); componentMap.Add(createComponentSet<SHSliderComponent, Slider>());
componentMap.Add(createComponentSet<SHTrajectoryRenderableComponent, TrajectoryRenderable>()); componentMap.Add(createComponentSet<SHTrajectoryRenderableComponent, TrajectoryRenderable>());
componentMap.Add(createComponentSet<SHAnimatorComponent, Animator>());
} }
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/

View File

@ -23,6 +23,9 @@ of DigiPen Institute of Technology is prohibited.
#include "Assets/MeshAsset.hxx" #include "Assets/MeshAsset.hxx"
#include "Scripts/Script.hxx" #include "Scripts/Script.hxx"
#include "Scripts/ScriptStore.hxx" #include "Scripts/ScriptStore.hxx"
#include "Assets/AnimationClipAsset.hxx"
#include "Assets/AnimationControllerAsset.hxx"
#include "Assets/AnimationRigAsset.hxx"
/*-------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------*/
/* File-Level Constants */ /* File-Level Constants */
@ -181,7 +184,10 @@ namespace SHADE
fieldInsertYaml<GameObject > (fieldInfo, object, fieldNode) || fieldInsertYaml<GameObject > (fieldInfo, object, fieldNode) ||
fieldInsertYaml<FontAsset > (fieldInfo, object, fieldNode) || fieldInsertYaml<FontAsset > (fieldInfo, object, fieldNode) ||
fieldInsertYaml<MaterialAsset > (fieldInfo, object, fieldNode) || fieldInsertYaml<MaterialAsset > (fieldInfo, object, fieldNode) ||
fieldInsertYaml<MeshAsset >(fieldInfo, object, fieldNode); fieldInsertYaml<MeshAsset > (fieldInfo, object, fieldNode) ||
fieldInsertYaml<AnimationClipAsset> (fieldInfo, object, fieldNode) ||
fieldInsertYaml<AnimationControllerAsset>(fieldInfo, object, fieldNode) ||
fieldInsertYaml<AnimationRigAsset> (fieldInfo, object, fieldNode);
// Serialization of more complex types // Serialization of more complex types
if (!PRIMITIVE_SERIALIZED) if (!PRIMITIVE_SERIALIZED)
@ -245,7 +251,10 @@ namespace SHADE
varInsertYamlInternal<GameObject >(object, fieldNode) || varInsertYamlInternal<GameObject >(object, fieldNode) ||
varInsertYamlInternal<FontAsset >(object, fieldNode) || varInsertYamlInternal<FontAsset >(object, fieldNode) ||
varInsertYamlInternal<MaterialAsset >(object, fieldNode) || varInsertYamlInternal<MaterialAsset >(object, fieldNode) ||
varInsertYamlInternal<MeshAsset >(object, fieldNode); varInsertYamlInternal<MeshAsset >(object, fieldNode) ||
varInsertYamlInternal<AnimationClipAsset >(object, fieldNode) ||
varInsertYamlInternal<AnimationRigAsset >(object, fieldNode) ||
varInsertYamlInternal<AnimationControllerAsset>(object, fieldNode);
return INSERTED; return INSERTED;
} }
@ -272,7 +281,10 @@ namespace SHADE
fieldAssignYaml<GameObject >(fieldInfo, object, node) || fieldAssignYaml<GameObject >(fieldInfo, object, node) ||
fieldAssignYaml<FontAsset >(fieldInfo, object, node) || fieldAssignYaml<FontAsset >(fieldInfo, object, node) ||
fieldAssignYaml<MaterialAsset >(fieldInfo, object, node) || fieldAssignYaml<MaterialAsset >(fieldInfo, object, node) ||
fieldAssignYaml<MeshAsset> (fieldInfo, object, node); fieldAssignYaml<MeshAsset >(fieldInfo, object, node) ||
fieldAssignYaml<AnimationClipAsset >(fieldInfo, object, node) ||
fieldAssignYaml<AnimationRigAsset >(fieldInfo, object, node) ||
fieldAssignYaml<AnimationControllerAsset>(fieldInfo, object, node);
if (!ASSIGNED) if (!ASSIGNED)
{ {
if (ReflectionUtilities::FieldIsList(fieldInfo)) if (ReflectionUtilities::FieldIsList(fieldInfo))
@ -346,7 +358,10 @@ namespace SHADE
varAssignYamlInternal<GameObject >(object, node) || varAssignYamlInternal<GameObject >(object, node) ||
varAssignYamlInternal<FontAsset >(object, node) || varAssignYamlInternal<FontAsset >(object, node) ||
varAssignYamlInternal<MaterialAsset >(object, node) || varAssignYamlInternal<MaterialAsset >(object, node) ||
varAssignYamlInternal<MeshAsset> (object, node); varAssignYamlInternal<MeshAsset >(object, node) ||
varAssignYamlInternal<AnimationClipAsset >(object, node) ||
varAssignYamlInternal<AnimationRigAsset >(object, node) ||
varAssignYamlInternal<AnimationControllerAsset>(object, node);
return DESERIALISED; return DESERIALISED;
} }
} }

View File

@ -62,7 +62,10 @@ namespace SHADE
} }
else if constexpr (std::is_same_v<FieldType, FontAsset> || else if constexpr (std::is_same_v<FieldType, FontAsset> ||
std::is_same_v<FieldType, MaterialAsset> || std::is_same_v<FieldType, MaterialAsset> ||
std::is_same_v<FieldType, MeshAsset>) std::is_same_v<FieldType, MeshAsset> ||
std::is_same_v<FieldType, AnimationClipAsset> ||
std::is_same_v<FieldType, AnimationControllerAsset> ||
std::is_same_v<FieldType, AnimationRigAsset>)
{ {
fieldNode = INVALID_ASSET_ID; fieldNode = INVALID_ASSET_ID;
} }
@ -130,7 +133,10 @@ namespace SHADE
} }
else if constexpr (std::is_same_v<FieldType, FontAsset> || else if constexpr (std::is_same_v<FieldType, FontAsset> ||
std::is_same_v<FieldType, MaterialAsset> || std::is_same_v<FieldType, MaterialAsset> ||
std::is_same_v<FieldType, MeshAsset>) std::is_same_v<FieldType, MeshAsset> ||
std::is_same_v<FieldType, AnimationClipAsset> ||
std::is_same_v<FieldType, AnimationControllerAsset> ||
std::is_same_v<FieldType, AnimationRigAsset>)
{ {
if (object->GetType() == FieldType::typeid) if (object->GetType() == FieldType::typeid)
{ {
@ -256,7 +262,10 @@ namespace SHADE
} }
else if constexpr (std::is_same_v<FieldType, FontAsset> || else if constexpr (std::is_same_v<FieldType, FontAsset> ||
std::is_same_v<FieldType, MaterialAsset> || std::is_same_v<FieldType, MaterialAsset> ||
std::is_same_v<FieldType, MeshAsset>) std::is_same_v<FieldType, MeshAsset> ||
std::is_same_v<FieldType, AnimationClipAsset> ||
std::is_same_v<FieldType, AnimationControllerAsset> ||
std::is_same_v<FieldType, AnimationRigAsset>)
{ {
if (object->GetType() == FieldType::typeid) if (object->GetType() == FieldType::typeid)
{ {