diff --git a/Assets/Animation Clips/MD_RigTest01_SkinningTestAnims.shanimcontainer b/Assets/Animation Clips/MD_RigTest01_SkinningTestAnims.shanimcontainer new file mode 100644 index 00000000..5d6924e2 Binary files /dev/null and b/Assets/Animation Clips/MD_RigTest01_SkinningTestAnims.shanimcontainer differ diff --git a/Assets/Animation Clips/MD_RigTest01_SkinningTestAnims.shanimcontainer.shmeta b/Assets/Animation Clips/MD_RigTest01_SkinningTestAnims.shanimcontainer.shmeta new file mode 100644 index 00000000..75860e48 --- /dev/null +++ b/Assets/Animation Clips/MD_RigTest01_SkinningTestAnims.shanimcontainer.shmeta @@ -0,0 +1,7 @@ +Name: MD_RigTest01_SkinningTestAnims +ID: 203438081 +Type: 12 +Sub Assets: +Name: Full +ID: 231416496 +Type: 13 diff --git a/Assets/Animation Clips/Main b/Assets/Animation Clips/Main new file mode 100644 index 00000000..ac5a59fa Binary files /dev/null and b/Assets/Animation Clips/Main differ diff --git a/Assets/Animation Clips/racoonAnims.shanimcontainer b/Assets/Animation Clips/racoonAnims.shanimcontainer new file mode 100644 index 00000000..c0b335cf Binary files /dev/null and b/Assets/Animation Clips/racoonAnims.shanimcontainer differ diff --git a/Assets/Animation Clips/racoonAnims.shanimcontainer.shmeta b/Assets/Animation Clips/racoonAnims.shanimcontainer.shmeta new file mode 100644 index 00000000..74ac2624 --- /dev/null +++ b/Assets/Animation Clips/racoonAnims.shanimcontainer.shmeta @@ -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 diff --git a/Assets/Scenes/anim.shade b/Assets/Scenes/anim.shade index aa42fc11..986195f6 100644 --- a/Assets/Scenes/anim.shade +++ b/Assets/Scenes/anim.shade @@ -10,6 +10,7 @@ Color: {x: 1, y: 1, z: 1, w: 1} Layer: 4294967295 Strength: 1 + Casting Shadows: false IsActive: true Scripts: ~ - EID: 2 @@ -18,16 +19,64 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 0, z: 0} - Rotate: {x: 0, y: 0, z: 0} + Translate: {x: -0.291508496, y: 0, z: 0} + Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true Renderable Component: - Mesh: 148542784 - Material: 121518381 + Mesh: 149697411 + Material: 128805346 IsActive: true Animator Component: - Rig: 76586906 - Clip: 76586906 + Rig: 77816045 + AnimationController: 0 IsActive: true - Scripts: ~ \ No newline at end of file + 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 + 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 \ No newline at end of file diff --git a/Assets/Scripts/AnimTest.cs b/Assets/Scripts/AnimTest.cs new file mode 100644 index 00000000..4ceccbe6 --- /dev/null +++ b/Assets/Scripts/AnimTest.cs @@ -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(); + } + + protected override void update() + { + // Play loop if shift is held + Action 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 + } +} diff --git a/Assets/Scripts/AnimTest.cs.shmeta b/Assets/Scripts/AnimTest.cs.shmeta new file mode 100644 index 00000000..45df5a57 --- /dev/null +++ b/Assets/Scripts/AnimTest.cs.shmeta @@ -0,0 +1,3 @@ +Name: AnimTest +ID: 165676130 +Type: 9 diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 92764ff2..3943b34d 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -179,6 +179,9 @@ namespace Sandbox // Link up SHDebugDraw SHDebugDraw::Init(SHSystemManager::GetSystem()); + + auto clip = SHResourceManager::LoadOrGet(77816045); + auto rig = SHResourceManager::LoadOrGet(77816045); } void SBApplication::Update(void) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index 934290a0..b4a62651 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -2,10 +2,10 @@ \file SHAnimationClip.cpp \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu -\date Nov 20, 2022 +\date Feb 27, 2023 \brief Contains the function definitions of the SHAnimationClip class. -Copyright (C) 2022 DigiPen Institute of Technology. +Copyright (C) 2023 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ @@ -13,61 +13,26 @@ of DigiPen Institute of Technology is prohibited. #include "SHpch.h" // Primary Header #include "SHAnimationClip.h" +#include "SHRawAnimation.h" namespace SHADE { /*-----------------------------------------------------------------------------------*/ /* Constructors */ /*-----------------------------------------------------------------------------------*/ - SHAnimationClip::SHAnimationClip(const SHAnimAsset& asset) + SHAnimationClip::SHAnimationClip(Handle rawAnimHandle, int firstFrame, int lastFrame) + : rawAnim { rawAnimHandle } + , startFrameIndex { firstFrame } + , endFrameIndex { lastFrame } + , duration { 0.0f } + , startTimeStamp { 0.0f } { - // 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{ posKey.time, posKey.value}); - } - for (const auto& rotKey : channel.rotationKeys) - { - newChannel.RotationKeyFrames.emplace_back(SHAnimationKeyFrame{ rotKey.time, rotKey.value}); - } - for (const auto& scaleKey : channel.scaleKeys) - { - newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame{ 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(maxFrames / totalTime); + if (!rawAnim) + return; + + const float SECS_PER_TICK = 1.0f / static_cast(rawAnim->GetTicksPerSecond()); + const int ONE_PAST_LAST_FRAME = lastFrame + 1; + duration = static_cast(ONE_PAST_LAST_FRAME - firstFrame) * SECS_PER_TICK; + startTimeStamp = static_cast(firstFrame) * SECS_PER_TICK; } - - /*-----------------------------------------------------------------------------------*/ - /* Usage Functions */ - /*-----------------------------------------------------------------------------------*/ - - /*-----------------------------------------------------------------------------------*/ - /* Helper Functions */ - /*-----------------------------------------------------------------------------------*/ - } \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index 03d6eee6..6b97c955 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -2,10 +2,10 @@ \file SHAnimationClip.h \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu -\date Dec 12, 2022 +\date Feb 27, 2023 \brief Contains the definition of the SHAnimationClip struct and related types. -Copyright (C) 2022 DigiPen Institute of Technology. +Copyright (C) 2023 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ @@ -14,71 +14,52 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "SH_API.h" #include "Math/SHMatrix.h" -#include "Assets/Asset Types/Models/SHAnimationAsset.h" +#include "Resource/SHHandle.h" namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + class SHRawAnimation; + /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ /// - /// Defines a single key frame in an animation for a specific type of data. - /// - template - struct SHAnimationKeyFrame - { - float TimeStamp; - T Data; - }; - - /// - /// Represents a animation clip of a 3D animation that is made for a specific model - /// rig. + /// Represents a snippet of 3D animation that is stored in a SHRawAnimation object. /// class SH_API SHAnimationClip { public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - /// - /// Defines the animations of a single bone in a rig. - /// - struct Channel - { - std::vector> PositionKeyFrames; - std::vector> RotationKeyFrames; - std::vector> ScaleKeyFrames; - int MaxFrames; - }; - /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ /// - /// Constructs an SHAnimation Clip from a specified SHAnimAsset. + /// Constructs an animation clip that contains the following parameters. /// - /// Animation asset to load. - explicit SHAnimationClip(const SHAnimAsset& asset); - + /// Handle to the raw animation data. + /// First frame to be played. + /// Last frame to be played. + SHAnimationClip(Handle rawAnimHandle, int firstFrame, int lastFrame); + /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - const std::vector& GetChannels() const noexcept { return channels; } - int GetTicksPerSecond() const noexcept { return ticksPerSecond; } - float GetTotalTime() const noexcept { return totalTime; } + inline Handle GetRawAnimation() const noexcept { return rawAnim; } + inline int GetStartFrameIndex() const noexcept { return startFrameIndex; } + inline int GetEndFrameIndex() const noexcept { return endFrameIndex; } + inline float GetTotalDuration() const noexcept { return duration; } + inline float GetStartTimeStamp() const noexcept { return startTimeStamp; } private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - std::vector channels; - int ticksPerSecond; - float totalTime; - - /*---------------------------------------------------------------------------------*/ - /* Helper Functions */ - /*---------------------------------------------------------------------------------*/ - + Handle rawAnim; + int startFrameIndex; // First Frame + int endFrameIndex; // Last Frame (inclusive) + float duration; // Total playback time + float startTimeStamp; // Starting time stamp of the raw anim }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimationController.cpp b/SHADE_Engine/src/Animation/SHAnimationController.cpp new file mode 100644 index 00000000..8152426f --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimationController.cpp @@ -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(testParam.Value != 0.0f); + case AnimParam::Type::Float: + return evaluateCondition(testParam.Value); + break; + case AnimParam::Type::Int: + return evaluateCondition(static_cast(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::CreateNode() + { + // Get system + auto system = SHSystemManager::GetSystem(); + if (system == nullptr) + throw std::runtime_error("[SHAnimationController] No SHAnimationSystem found!"); + + // Construct + auto node = system->GetResourceHub().Create(); + 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) + { + // 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 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 newNode) + { + instData.CurrentNode = newNode; + instData.ClipPlaybackTime = 0.0f; + } +} diff --git a/SHADE_Engine/src/Animation/SHAnimationController.h b/SHADE_Engine/src/Animation/SHAnimationController.h new file mode 100644 index 00000000..56bf6f45 --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimationController.h @@ -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 +#include +#include +// Project Includes +#include "SH_API.h" +#include "Resource/SHHandle.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + class SHAnimationClip; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /// + /// 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! + /// + class SH_API SHAnimationController + { + public: + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + struct Node; + + /*---------------------------------------------------------------------------------*/ + /* Type Definition */ + /*---------------------------------------------------------------------------------*/ + /// + /// Describes a parameter for the AnimationController that can be used to control + /// the flow of animations. + /// + struct SH_API AnimParam + { + /*-------------------------------------------------------------------------------*/ + /* Type Definition */ + /*-------------------------------------------------------------------------------*/ + /// + /// Type of animation parameter. + /// + 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 */ + /*-------------------------------------------------------------------------------*/ + /// + /// Constructs an AnimParam with the default value set for the Value field based + /// on the specified type. + /// + /// Type of AnimParam. + explicit AnimParam(Type type = Type::Int); + + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + Type ParamType; + ValueType Value; + }; + + /// + /// Describes a transition between nodes of the animation controller. + /// + struct Transition + { + /*-------------------------------------------------------------------------------*/ + /* Type Definition */ + /*-------------------------------------------------------------------------------*/ + /// + /// Types of conditions for the transition. + /// + enum class ConditionType + { + None, + Equals, + NotEquals, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual + }; + + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + Handle Target; + ConditionType Condition = ConditionType::None; + AnimParam Param; + std::string ParamName; + + /*-------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-------------------------------------------------------------------------------*/ + /// + /// Checks the condition of this Transition against an animation paramter. + /// + /// Parameter to test with. + /// Whether the condition passed. + bool EvaluateCondition(const AnimParam& testParam) const noexcept; + + private: + /*-------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-------------------------------------------------------------------------------*/ + template + bool evaluateCondition(T value) const noexcept; + }; + + /// + /// Describes a node in the animation controller. + /// + struct Node + { + std::string Name = "Unnamed Node"; + Handle Clip; + std::vector Transitions; + }; + + /// + /// Describes a node in the animation controller. + /// + struct InstanceData + { + Handle CurrentNode; + std::unordered_map Params; + float ClipPlaybackTime; + }; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + Handle StartingNode; + + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Runs a single update for the animation controller. + /// + void Update(InstanceData& instData, float dt); + + /*---------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Creates a node in the state machine. Created nodes must be destroyed using + /// DestroyNode(). + /// + /// Node that was created. + Handle CreateNode(); + /// + /// Destroys the node that was created in the state machine. + /// + /// Node to destroy. + void DestroyNode(Handle node); + /// + /// Links two nodes together with a Transition. This performs some additional + /// checking to ensure parameters are valid. + /// + /// Source node to transition from. + /// Describes the transition to add. + void AddTransition(Handle source, const Transition& transition); + /// + /// Registers a parameter to the animation controller. + /// + /// Name of the parameter. + /// Type of the parameter. + void AddParameter(const std::string& name, AnimParam::Type type); + /// + /// Removes a parameter from the animation controller. + /// + /// Name of the parameter. + void RemoveParameter(const std::string& name); + /// + /// Sets the parameter of the for the string. Does nothing if an invalid param name + /// is provided. Type of the parameter is not checked. + /// + /// + /// Type of parameter. Only bool, int, floats are supported. + /// + /// Data of the instance to update. + /// Name of the parameter. + /// Value to set the parameter to. + template + void SetParameter(InstanceData& instData, const std::string& paramName, T value); + /// + /// Gets the parameter of the for the string. Types are checked and will not return + /// a value if there is nothing. + /// + /// + /// Type of parameter. Only bool, int, floats are supported. + /// + /// Data of the instance to update. + /// Name of the parameter. + /// The value of the parameter or nothing if invalid. + template + std::optional GetParameter(InstanceData& instData, const std::string& paramName); + /// + /// 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. + /// + /// Data of the instance to update. + /// Name of the parameter. + void SetTrigger(InstanceData& instData, const std::string& paramName); + + /*---------------------------------------------------------------------------------*/ + /* Getters */ + /*---------------------------------------------------------------------------------*/ + const std::unordered_map& GetParams() const noexcept { return parameters; } + const std::vector>& GetNodes() const noexcept { return nodes; } + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + // State machine + std::vector> nodes; + std::unordered_map parameters; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void changeNode(InstanceData& instData, Handle newNode); + }; +} + +#include "SHAnimationController.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimationController.hpp b/SHADE_Engine/src/Animation/SHAnimationController.hpp new file mode 100644 index 00000000..7b86f125 --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimationController.hpp @@ -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 + bool SHAnimationController::Transition::evaluateCondition(T value) const noexcept + { + // Get the value + const T PARAM_VAL = [&]() + { + if constexpr (std::is_floating_point_v) + 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) + { + static constexpr T EPSILON = static_cast(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) + { + static constexpr T EPSILON = static_cast(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 + void SHAnimationController::SetParameter(InstanceData& instData, const std::string& paramName, T value) + { + static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "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 + std::optional SHAnimationController::GetParameter(InstanceData& instData, const std::string& paramName) + { + static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "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) + { + if (TYPE != AnimParam::Type::Bool && TYPE != AnimParam::Type::Trigger) + return {}; + } + else if constexpr (std::is_same_v) + { + if (parameters[paramName] != AnimParam::Type::Float) + return {}; + } + else if constexpr (std::is_same_v) + { + 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 + } + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.h b/SHADE_Engine/src/Animation/SHAnimationSystem.h index 3d46edc2..9c2cd073 100644 --- a/SHADE_Engine/src/Animation/SHAnimationSystem.h +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.h @@ -15,6 +15,7 @@ of DigiPen Institute of Technology is prohibited. #include "SH_API.h" #include "ECS_Base/System/SHSystem.h" #include "ECS_Base/System/SHSystemRoutine.h" +#include "Resource/SHResourceLibrary.h" namespace SHADE { @@ -51,5 +52,16 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ virtual void Init(void) override final; virtual void Exit(void) override final; + + /*---------------------------------------------------------------------------------*/ + /* Getters */ + /*---------------------------------------------------------------------------------*/ + SHResourceHub& GetResourceHub() { return resources; } + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + SHResourceHub resources; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 04d355b8..d21ee5e1 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -33,18 +33,62 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ void SHAnimatorComponent::Play() { - isPlaying = false; + isPlaying = true; + playOnce = false; } void SHAnimatorComponent::Play(Handle clip) { + // Ignore if nothing is specified + if (!clip) + { + SHLOG_WARNING("[SHAnimatorComponent] Attempted to play an null SHAnimationClip. Use Stop() if stopping animation instead."); + return; + } + + // Remove animation controller as we switch to manual play mode + animController = {}; + animInstanceData.CurrentNode = {}; + animInstanceData.Params.clear(); + animInstanceData.ClipPlaybackTime = 0.0f; + + // Set accordingly currClip = clip; currPlaybackTime = 0.0f; - Play(); + 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(); + + // Set to initial pose + if (rig && rig->GetRootNode()) + { + updateCurrentAnimatorState(currClip, 0.0f); + } + } + } + + void SHAnimatorComponent::PlayOneShot(Handle clip) + { + Play(clip); + playOnce = true; } void SHAnimatorComponent::PlayFromStart() { + if (!currClip) + { + SHLOG_WARNING("[SHAnimatorComponent] Attempted to restart a clip but there is no existing clip. Ignored."); + return; + } + isPlaying = true; currPlaybackTime = 0.0f; } @@ -56,10 +100,47 @@ namespace SHADE void SHAnimatorComponent::Stop() { + if (!currClip) + { + SHLOG_WARNING("[SHAnimatorComponent] Attempted to stop a clip but there is no existing clip. Ignored."); + return; + } + isPlaying = false; 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 */ /*-----------------------------------------------------------------------------------*/ @@ -79,60 +160,118 @@ namespace SHADE } } - void SHAnimatorComponent::SetClip(Handle newClip) + void SHAnimatorComponent::SetAnimationController(Handle ac) { // No change - if (currClip == newClip) + if (animController == ac) return; - // Set parameters - currClip = newClip; - secsPerTick = 1.0f / currClip->GetTicksPerSecond(); + // Set the controller + animController = ac; - // Set to initial pose - if (rig && rig->GetRootNode() && currClip) + // If valid, we want to initialize it + 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> rawAnims; + for (auto node : animController->GetNodes()) + { + // Ensure no null handles + if (!node) + continue; + const Handle CLIP = node->Clip; + if (!CLIP) + continue; + const Handle RAW_ANIM = CLIP->GetRawAnimation(); + if (!RAW_ANIM) + continue; + + // Store + rawAnims.emplace(RAW_ANIM); + } } } - - /*-----------------------------------------------------------------------------------*/ - /* Update Functions */ - /*-----------------------------------------------------------------------------------*/ - void SHAnimatorComponent::Update(float dt) + + void SHAnimatorComponent::SetTrigger(const std::string& paramName) { - //Reset matrices - std::fill(boneMatrices.begin(), boneMatrices.end(), SHMatrix::Identity); - - // Nothing to animate - if (!currClip || !isPlaying || !rig || !rig->GetRootNode()) + if (!animController) return; - // Update time on the playback - currPlaybackTime += dt; - if (currPlaybackTime > currClip->GetTotalTime()) - { - currPlaybackTime = currPlaybackTime - currClip->GetTotalTime(); - } - - // Play the clip - updatePoseWithClip(currPlaybackTime); + return animController->SetTrigger(animInstanceData, paramName); } /*-----------------------------------------------------------------------------------*/ - /* Helper Functions */ + /* Helper Functions - Update */ /*-----------------------------------------------------------------------------------*/ - void SHAnimatorComponent::updatePoseWithClip(float poseTime) + void SHAnimatorComponent::updateAnimController(float dt) { - updatePoseWithClip(poseTime, rig->GetRootNode(), SHMatrix::Identity); + // 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; + const float CLIP_CURR_PLAYED_TIME = currPlaybackTime - currClip->GetStartTimeStamp(); + if (CLIP_CURR_PLAYED_TIME > currClip->GetTotalDuration()) + { + if (playOnce) + { + playOnce = false; + isPlaying = false; + currPlaybackTime = currClip->GetStartTimeStamp() + currClip->GetTotalDuration(); + } + else + { + currPlaybackTime = currClip->GetStartTimeStamp(); + } + } + } + void SHAnimatorComponent::updateCurrentAnimatorState(Handle clip, float playbackTime) + { + // Nothing to animate + if (!clip || !isPlaying || !rig || !rig->GetRootNode()) + return; + + // Check that we have animation data + if (!clip->GetRawAnimation()) + return; + + // Play the clip + updatePoseWithClip(clip, playbackTime); + } + void SHAnimatorComponent::updatePoseWithClip(Handle clip, float poseTime) + { + updatePoseWithClip(poseTime, clip->GetRawAnimation(), rig->GetRootNode(), SHMatrix::Identity); } - void SHAnimatorComponent::updatePoseWithClip(float poseTime, Handle node, const SHMatrix& parentMatrix) + void SHAnimatorComponent::updatePoseWithClip(float poseTime, Handle rawAnimData, Handle node, const SHMatrix& parentMatrix) { // Check if there is a channel for this node SHMatrix transformMatrix = node->TransformMatrix; const int BONE_IDX = rig->GetNodeIndex(node); - const auto& CHANNELS = currClip->GetChannels(); + const auto& CHANNELS = rawAnimData->GetChannels(); if (BONE_IDX < CHANNELS.size()) { const auto& CHANNEL = CHANNELS[BONE_IDX]; @@ -149,16 +288,15 @@ namespace SHADE // Apply transformations to this node const int BONE_MTX_IDX = rig->GetNodeIndex(node); - std::optional position; if (BONE_MTX_IDX >= 0) { boneMatrices[BONE_MTX_IDX] = node->OffsetMatrix * transformMatrix; } // Apply pose to children - for (auto& child : node->Children) + for (auto child : node->Children) { - updatePoseWithClip(poseTime, child, transformMatrix); + updatePoseWithClip(poseTime, rawAnimData, child, transformMatrix); } } } diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index d6c05135..01e50861 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -22,7 +22,8 @@ of DigiPen Institute of Technology is prohibited. #include "Math/SHMatrix.h" #include "Math/Vector/SHVec3.h" #include "Math/SHQuaternion.h" -#include "SHAnimationClip.h" +#include "SHRawAnimation.h" +#include "SHAnimationController.h" namespace SHADE { @@ -32,7 +33,6 @@ namespace SHADE class SHRig; struct SHRigNode; class SHAnimationClip; - class SHVkBuffer; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -52,12 +52,20 @@ namespace SHADE /// void Play(); /// - /// 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. /// - /// + /// Animation clip to play. void Play(Handle clip); /// - /// 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. + /// + /// Animation clip to play. + void PlayOneShot(Handle clip); + /// + /// Plays the currently loaded animation clip from the start. Note that this only + /// works when using manual playback mode. /// void PlayFromStart(); /// @@ -65,10 +73,22 @@ namespace SHADE /// void Pause(); /// - /// 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. /// void Stop(); + /*---------------------------------------------------------------------------------*/ + /* Update Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// 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. + /// + /// Time passed since the last frame. + void Update(float dt); + /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ @@ -78,12 +98,44 @@ namespace SHADE /// Rig to use. void SetRig(Handle newRig); /// - /// Sets the animation clip of this animator without playing it. - /// This will set the pose of the model to it's initial pose. - /// If the clip is the same as the current clip, nothing happens. + /// Sets the animation controller to use for this animator. /// - /// Clip to use. - void SetClip(Handle newClip); + /// Animation controller to use. + void SetAnimationController(Handle ac); + + /*---------------------------------------------------------------------------------*/ + /* Parameter Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// 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. + /// + /// + /// Type of parameter. Only bool, int, floats are supported. + /// + /// Name of the parameter. + /// Value to set the parameter to. + template + void SetParameter(const std::string& paramName, T value); + /// + /// 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. + /// + /// + /// Type of parameter. Only bool, int, floats are supported. + /// + /// Name of the parameter. + /// The value of the parameter or nothing if invalid. + template + std::optional GetParameter(const std::string& paramName); + /// + /// 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. + /// + /// Name of the parameter. + void SetTrigger(const std::string& paramName); /*---------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -99,38 +151,42 @@ namespace SHADE /// Handle to the currently set rig. Handle GetRig() const noexcept { return rig; } /// - /// - /// Retrieve the currently set animation clip. - /// - /// Handle to the currently set animation clip. - Handle GetCurrentClip() const noexcept { return currClip; } - /// /// Checks if an animation is currently playing. /// /// True if an animation clip is currently playing. bool IsPlaying() const { return isPlaying; } - - /*---------------------------------------------------------------------------------*/ - /* Update Functions */ - /*---------------------------------------------------------------------------------*/ /// - /// Updates the current state of the animation if one is specified based on the - /// current animation clip and frames. This will update the bone matrices. + /// Retrieves the current node for the Animation Controller. This returns a null + /// if there is no Animation Controller currently set. /// - /// Time passed since the last frame. - void Update(float dt); + /// Handle to the current Animation Controller node. + Handle GetCurrentNode() const noexcept { return animInstanceData.CurrentNode; } + /// + /// Retrieves the currently set animation controller. + /// + /// + Handle GetAnimationController() const noexcept { return animController; } private: + /*---------------------------------------------------------------------------------*/ + /* Type Definition */ + /*---------------------------------------------------------------------------------*/ + using ChannelMap = std::unordered_map; + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ // Resources Handle rig; + // Playback Tracking for Animation Controller Mode + Handle animController; + SHAnimationController::InstanceData animInstanceData; + // Playback Tracking for Manual Mode Handle currClip; - // Playback Tracking float currPlaybackTime = 0.0f; + bool playOnce = false; + // Shared Tracking bool isPlaying = true; - // Useful Cached Data float secsPerTick = 0.0f; // Buffer std::vector boneMatrices; @@ -138,8 +194,11 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - void updatePoseWithClip(float poseTime); - void updatePoseWithClip(float poseTime, Handle node, const SHMatrix& parentMatrix); + void updateAnimController(float dt); + void updateManualClipState(float dt); + void updateCurrentAnimatorState(Handle clip, float playbackTime); + void updatePoseWithClip(Handle clip, float poseTime); + void updatePoseWithClip(float poseTime, Handle rawAnimData, Handle node, const SHMatrix& parentMatrix); template T getInterpolatedValue(const std::vector>& keyframes, float poseTime); diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp index 82ed0600..8c8bc09f 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp @@ -15,13 +15,31 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "SHRig.h" #include "Math/SHMatrix.h" -#include "SHAnimationClip.h" -#include "Graphics/SHVkUtil.h" -#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" -#include "ECS_Base/Managers/SHSystemManager.h" +#include "SHRawAnimation.h" +#include "SHAnimationController.h" namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*-----------------------------------------------------------------------------------*/ + template + std::optional SHAnimatorComponent::GetParameter(const std::string& paramName) + { + if (!animController) + return {}; + + return animController->GetParameter(animInstanceData, paramName); + } + template + void SHAnimatorComponent::SetParameter(const std::string& paramName, T value) + { + if (!animController) + return; + + return animController->SetParameter(animInstanceData, paramName, value); + } + /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHRawAnimation.cpp b/SHADE_Engine/src/Animation/SHRawAnimation.cpp new file mode 100644 index 00000000..6f77f958 --- /dev/null +++ b/SHADE_Engine/src/Animation/SHRawAnimation.cpp @@ -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{ posKey.time, posKey.value}); + } + for (const auto& rotKey : channel.rotationKeys) + { + newChannel.RotationKeyFrames.emplace_back(SHAnimationKeyFrame{ rotKey.time, rotKey.value}); + } + for (const auto& scaleKey : channel.scaleKeys) + { + newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame{ 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(maxFrames / totalTime); + } + + totalFrames = maxFrames; + } + + /*-----------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------------*/ + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHRawAnimation.h b/SHADE_Engine/src/Animation/SHRawAnimation.h new file mode 100644 index 00000000..ce8617ad --- /dev/null +++ b/SHADE_Engine/src/Animation/SHRawAnimation.h @@ -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 */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Defines a single key frame in an animation for a specific type of data. + /// + template + struct SHAnimationKeyFrame + { + float TimeStamp; + T Data; + }; + + /// + /// Represents the raw 3D animation data for a rigged 3D model. + /// + class SH_API SHRawAnimation + { + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Defines the animations of a single bone in a rig. + /// + struct Channel + { + std::vector> PositionKeyFrames; + std::vector> RotationKeyFrames; + std::vector> ScaleKeyFrames; + int MaxFrames; + }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + /// + /// Constructs an SHAnimation Clip from a specified SHAnimAsset. + /// + /// Animation asset to load. + explicit SHRawAnimation(const SHAnimAsset& asset); + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + const std::vector& 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 channels; + int ticksPerSecond; + float totalTime; + int totalFrames; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h index 8b6db749..359e2c6b 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h @@ -10,8 +10,10 @@ *****************************************************************************/ #pragma once -#include "Math/SHMath.h" #include "Assets/Asset Types/SHAssetData.h" +#include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" +#include "Math/SHQuaternion.h" #include diff --git a/SHADE_Engine/src/Assets/Asset Types/SHAnimClipContainerAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHAnimClipContainerAsset.h new file mode 100644 index 00000000..733119c0 --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/SHAnimClipContainerAsset.h @@ -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 + +#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 clips{}; + }; +} diff --git a/SHADE_Engine/src/Assets/Asset Types/SHAnimControllerAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHAnimControllerAsset.h new file mode 100644 index 00000000..625e4d5c --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/SHAnimControllerAsset.h @@ -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 + +#include "SH_API.h" +#include "SHAssetData.h" + +namespace SHADE +{ + struct SH_API SHAnimControllerAsset : SHAssetData + { + std::string name; + // TODO + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHBinaryLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHBinaryLoader.cpp new file mode 100644 index 00000000..b59f6807 --- /dev/null +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHBinaryLoader.cpp @@ -0,0 +1,128 @@ +#include "SHpch.h" +#include "SHBinaryLoader.h" + +#include "Assets/Asset Types/SHAnimClipContainerAsset.h" + +#include + +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(data); + file.write( + reinterpret_cast(&anim.animRawDataAssetId), + sizeof(uint32_t) + ); + + uint32_t const size {static_cast(anim.clips.size())}; + + file.write( + reinterpret_cast(&size), + sizeof(uint32_t) + ); + + for (auto const& clip : anim.clips) + { + uint32_t charCount {static_cast(clip.name.size())}; + file.write( + reinterpret_cast(&charCount), + sizeof(uint32_t) + ); + + file.write( + clip.name.data(), + charCount + ); + + file.write( + reinterpret_cast(&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(&data->animRawDataAssetId), + sizeof(uint32_t) + ); + + uint32_t size; + + file.read( + reinterpret_cast(&size), + sizeof(uint32_t) + ); + + for (auto i{0}; i < size; ++i) + { + auto& clip {data->clips.emplace_back()}; + uint32_t charCount; + file.read( + reinterpret_cast(&charCount), + sizeof(uint32_t) + ); + + clip.name.resize(charCount); + file.read( + clip.name.data(), + charCount + ); + + file.read( + reinterpret_cast(&clip.firstIndex), + sizeof(uint32_t) * 2 + ); + + clip.animRawDataAssetId = data->animRawDataAssetId; + } + + result = data; + } +} diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHBinaryLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHBinaryLoader.h new file mode 100644 index 00000000..5689901d --- /dev/null +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHBinaryLoader.h @@ -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); + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.cpp index 01fe6e6b..f669ddd4 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.cpp @@ -35,32 +35,32 @@ namespace SHADE 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; } + std::stringstream stream; - stream << file.rdbuf(); - std::string content = stream.str(); + auto const extension = path.extension().string(); SHAssetData* result; - if (path.extension().string() == SCENE_EXTENSION) + if (extension == SCENE_EXTENSION) { auto data = new SHSceneAsset(); data->name = path.stem().string(); data->data = std::move(content); result = data; } - else if (path.extension().string() == PREFAB_EXTENSION) + else if (extension == PREFAB_EXTENSION) { auto data = new SHPrefabAsset(); data->name = path.stem().string(); data->data = std::move(content); result = data; } - else if (path.extension().string() == MATERIAL_EXTENSION) + else if (extension == MATERIAL_EXTENSION) { auto data = new SHMaterialAsset(); data->name = path.stem().string(); @@ -79,21 +79,23 @@ namespace SHADE 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; } - if (path.extension().string() == SCENE_EXTENSION) + auto const extension = path.extension().string(); + + if (extension == SCENE_EXTENSION) { auto scene = dynamic_cast(data); file << scene->data; } - else if (path.extension().string() == PREFAB_EXTENSION) + else if (extension == PREFAB_EXTENSION) { auto prefab = dynamic_cast(data); file << prefab->data; } - else if (path.extension().string() == MATERIAL_EXTENSION) + else if (extension == MATERIAL_EXTENSION) { auto material = dynamic_cast(data); file << material->data; diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h index b74c6c94..80771058 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h @@ -11,10 +11,6 @@ #pragma once #include "SHAssetLoader.h" -#include "Assets/Asset Types/SHPrefabAsset.h" -#include "Assets/Asset Types/SHSceneAsset.h" -#include "Assets/Asset Types/SHMaterialAsset.h" - namespace SHADE { struct SHTextBasedLoader : SHAssetLoader diff --git a/SHADE_Engine/src/Assets/SHAssetMacros.h b/SHADE_Engine/src/Assets/SHAssetMacros.h index 581009c8..f12cc56f 100644 --- a/SHADE_Engine/src/Assets/SHAssetMacros.h +++ b/SHADE_Engine/src/Assets/SHAssetMacros.h @@ -7,6 +7,7 @@ * or disclosure of this file or its contents without the prior * written consent of Digipen Institute of Technology is prohibited ******************************************************************************/ +// ReSharper disable All #ifndef SH_ASSET_MACROS_H #define SH_ASSET_MACROS_H @@ -18,22 +19,22 @@ // FMOD Fwd Declare namespace FMOD { - class Sound; - class System; - class ChannelGroup; - class Channel; + class Sound; + class System; + class ChannelGroup; + class Channel; } enum FMOD_RESULT : int; enum FMOD_SPEAKERMODE : int; // Typedefs typedef uint32_t AssetID; -typedef std::string AssetName; +using AssetName = std::string; typedef std::filesystem::path AssetPath; typedef unsigned char* AssetData; typedef std::string AssetMetaVersion; typedef std::string AssetExtension; -typedef size_t AssetTypeMeta; +typedef size_t AssetTypeMeta; typedef FMOD::Sound* SHSound; @@ -45,9 +46,9 @@ constexpr AssetID INVALID_ASSET_ID {0}; // Asset type enum enum class AssetType : AssetTypeMeta { - INVALID, - SHADER, - SHADER_BUILT_IN, + INVALID, + SHADER, + SHADER_BUILT_IN, TEXTURE, MODEL, SCENE, @@ -57,7 +58,10 @@ enum class AssetType : AssetTypeMeta SCRIPT, FONT, AUDIO_BANK, - MAX_COUNT + ANIM_CONTAINER, + ANIM_CLIP, + ANIM_CONTROLLER, + MAX_COUNT }; constexpr size_t TYPE_COUNT{ static_cast(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 PREFAB_FOLDER{ "/Prefabs/" }; 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 @@ -94,20 +100,26 @@ constexpr std::string_view PREFAB_EXTENSION {".shprefab"}; constexpr std::string_view MATERIAL_EXTENSION {".shmat"}; constexpr std::string_view TEXTURE_EXTENSION {".shtex"}; 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[] = { - AUDIO_EXTENSION, - SHADER_EXTENSION, - SHADER_BUILT_IN_EXTENSION, + AUDIO_EXTENSION, + SHADER_EXTENSION, + SHADER_BUILT_IN_EXTENSION, TEXTURE_EXTENSION, MODEL_EXTENSION, SCENE_EXTENSION, - PREFAB_EXTENSION, + PREFAB_EXTENSION, MATERIAL_EXTENSION, - "dummy", + FILLER_EXTENSION, SCRIPT_EXTENSION, FONT_EXTENSION, - AUDIO_BANK_EXTENSION + AUDIO_BANK_EXTENSION, + ANIM_CONTAINER_EXTENSION, + ANIM_CONTROLLER_EXTENSION, + FILLER_EXTENSION }; constexpr size_t EXTENSIONS_COUNT{ static_cast(AssetType::MAX_COUNT) }; @@ -120,10 +132,10 @@ constexpr std::string_view GLTF_EXTENSION{ ".gltf" }; constexpr std::string_view TTF_EXTENSION{ ".ttf" }; constexpr std::string_view EXTERNALS[] = { - GLSL_EXTENSION, - DDS_EXTENSION, - FBX_EXTENSION, - GLTF_EXTENSION, + GLSL_EXTENSION, + DDS_EXTENSION, + FBX_EXTENSION, + GLTF_EXTENSION, TTF_EXTENSION }; @@ -133,9 +145,9 @@ constexpr std::string_view FRAGMENT_SHADER{ "_FS" }; constexpr std::string_view COMPUTER_SHADER{ "_CS" }; constexpr std::pair SHADER_IDENTIFIERS[] = { - std::make_pair(VERTEX_SHADER, SHADE::SH_SHADER_TYPE::VERTEX), - std::make_pair(FRAGMENT_SHADER, SHADE::SH_SHADER_TYPE::FRAGMENT), - std::make_pair(COMPUTER_SHADER, SHADE::SH_SHADER_TYPE::COMPUTE) + std::make_pair(VERTEX_SHADER, SHADE::SH_SHADER_TYPE::VERTEX), + std::make_pair(FRAGMENT_SHADER, SHADE::SH_SHADER_TYPE::FRAGMENT), + std::make_pair(COMPUTER_SHADER, SHADE::SH_SHADER_TYPE::COMPUTE) }; constexpr size_t SHADER_TYPE_MAX_COUNT{ 3 }; diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index a611cf68..fcd9c5b1 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -21,8 +21,13 @@ #include "Libraries/Loaders/SHShaderSourceLoader.h" #include "Libraries/Loaders/SHTextBasedLoader.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/SHShaderSourceCompiler.h" @@ -159,14 +164,24 @@ namespace SHADE return AssetType::INVALID; } - std::optional SHAssetManager::GetAsset(AssetID id) noexcept + SHAsset* SHAssetManager::GetAsset(AssetID id) noexcept { 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 @@ -233,8 +248,19 @@ namespace SHADE } break; + case AssetType::ANIM_CONTAINER: + newPath += ANIM_CLIP_FOLDER; + newPath += name; + newPath += ANIM_CONTAINER_EXTENSION; + + { + auto animClip = new SHAnimClipContainerAsset(); + data = animClip; + } + break; + 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; } @@ -249,13 +275,13 @@ namespace SHADE auto result = assetCollection.emplace( id, - SHAsset( - name, - id, - type, - newPath, - false - ) + SHAsset( + name, + id, + type, + newPath, + false + ) ); assetData.emplace(id, data); @@ -266,6 +292,41 @@ namespace SHADE 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(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 { if (assetCollection.contains(id)) @@ -274,7 +335,8 @@ namespace SHADE if ( asset.type == AssetType::SCENE || asset.type == AssetType::PREFAB || - asset.type == AssetType::MATERIAL + asset.type == AssetType::MATERIAL || + asset.type == AssetType::ANIM_CONTAINER ) { if (assetData.contains(id)) @@ -467,9 +529,9 @@ namespace SHADE //Reload data auto result = GetAsset(target); - if (result.has_value()) + if (result) { - auto const& asset{ result.value() }; + auto const& asset{ *result }; auto newData = loaders[static_cast(asset.type)]->Load(asset.path); delete assetData[target]; assetData[target] = newData; @@ -533,6 +595,8 @@ namespace SHADE loaders[static_cast(AssetType::SCRIPT)] = nullptr; loaders[static_cast(AssetType::FONT)] = dynamic_cast(new SHFontLoader()); loaders[static_cast(AssetType::AUDIO_BANK)] = loaders[static_cast(AssetType::SCENE)]; + loaders[static_cast(AssetType::ANIM_CONTAINER)] = dynamic_cast(new SHBinaryLoader()); + loaders[static_cast(AssetType::ANIM_CLIP)] = nullptr; } /**************************************************************************** @@ -590,16 +654,37 @@ namespace SHADE else { assetData.emplace(parent.id, parentData); - if (parent.type == AssetType::MODEL) + switch(parent.type) { - auto parentModel = reinterpret_cast(parentData); - for (auto i {0}; i < parent.subAssets.size(); ++i) + case AssetType::MODEL: { - assetData.emplace( - parent.subAssets[i]->id, - parentModel->meshes[i] - ); + const auto parentModel = reinterpret_cast(parentData); + for (auto i {0}; i < parent.subAssets.size(); ++i) + { + assetData.emplace( + parent.subAssets[i]->id, + parentModel->meshes[i] + ); + } } + break; + + case AssetType::ANIM_CONTAINER: + { + const auto parentContainer = reinterpret_cast(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]; @@ -757,6 +842,38 @@ namespace SHADE 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(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 @@ -813,7 +930,9 @@ namespace rttr value("Material", AssetType::MATERIAL), value("Mesh", AssetType::MESH), value("Script", AssetType::SCRIPT), - value("Font", AssetType::FONT) + value("Font", AssetType::FONT), + value("Animation Container", AssetType::ANIM_CONTAINER), + value("Animation Clip", AssetType::ANIM_CLIP) ); } } diff --git a/SHADE_Engine/src/Assets/SHAssetManager.h b/SHADE_Engine/src/Assets/SHAssetManager.h index e5cd0359..d8a48afd 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.h +++ b/SHADE_Engine/src/Assets/SHAssetManager.h @@ -50,7 +50,8 @@ namespace SHADE * \return const& to unordered_map ****************************************************************************/ static std::vector GetAllAssets() noexcept; - static std::optional GetAsset(AssetID id) noexcept; + static SHAsset* GetAsset(AssetID id) noexcept; + static SHAsset const* GetAssetConst(AssetID id) noexcept; static AssetType GetType(AssetID id) noexcept; @@ -63,6 +64,7 @@ namespace SHADE * \return resource id generated for new asset ****************************************************************************/ 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 DeleteAsset(AssetID id) noexcept; diff --git a/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.cpp b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.cpp new file mode 100644 index 00000000..17b252f9 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.cpp @@ -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 +// External Dependencies +#include +#include +// 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(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(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(param.second)].c_str(), ImGuiComboFlags_None)) + { + // All other options + for (int i = 0; i < static_cast(typesList.size()); ++i) + { + const bool IS_SELECTED = static_cast(param.second) == i; + + if (ImGui::Selectable(typesList[i].c_str(), IS_SELECTED)) + { + SHCommandManager::PerformCommand + ( + std::reinterpret_pointer_cast + ( + std::make_shared> + ( + param.second, + static_cast(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(node.Clip).value_or(""); + ImGui::SetNextItemWidth(NODE_WIDTH); + SHEditorWidgets::DragDropReadOnlyField + ( + "Clip", CLIP_NAME, + [&]() + { + return SHResourceManager::GetAssetID(node.Clip).value_or(0); + }, + [&](AssetID id) + { + if (SHAssetManager::GetType(id) != AssetType::ANIM_CLIP) + return; + node.Clip = SHResourceManager::LoadOrGet(id); + SHResourceManager::FinaliseChanges(); + }, + SHDragDrop::DRAG_RESOURCE, {}, NODE_WIDTH + ); + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + { + if (node.Clip) + { + AssetID assetID = SHResourceManager::GetAssetID(node.Clip).value_or(0); + SHEditorWindowManager::GetEditorWindow()->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(sourceAttrib); + destAttribIndex.Raw = static_cast(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 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 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 + ( + std::make_shared> + ( + 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 + ( + std::make_shared> + ( + 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(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(SHAnimationController::Transition::ConditionType::NotEquals) + : static_cast(conditionsList.size() - 1); + // Comparisons + for (int i = 0; i <= LAST_ELEM; ++i) + { + const bool IS_SELECTED = i == static_cast(linkData.Condition); + if (ImGui::Selectable(conditionsList[i].c_str(), IS_SELECTED)) + { + SHCommandManager::PerformCommand + ( + std::reinterpret_pointer_cast + ( + std::make_shared> + ( + linkData.Condition, + static_cast(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(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(linkData.ParamThresholdValue); }, + [&](int val) { linkData.ParamThresholdValue = static_cast(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::lowest(); + return extraInputAttrib; + } + SHAnimationControllerEditor::NodeAttributeIndex SHAnimationControllerEditor::getExtraOutputAttrib(uint32_t nodeIndex) + { + NodeAttributeIndex extraOutputAttrib; + extraOutputAttrib.OwnerNodeIndex = nodeIndex; + extraOutputAttrib.AttributeIndex = std::numeric_limits::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 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 selectedLinks(NUM_SELECTED_LINKS); + ImNodes::GetSelectedLinks(selectedLinks.data()); + + for (auto linkId : selectedLinks) + { + deleteLink(controllerData.value(), linkId); + } + } + } + std::list::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::iterator sourceNode, std::list::iterator destNode) + { + // Update source node's output attributes + NodeAttributeIndex attribIndex; + attribIndex.OwnerNodeIndex = sourceNode->Index; + attribIndex.AttributeIndex = static_cast(sourceNode->OutputAttribs.size() + 1); + sourceNode->OutputAttribs.emplace_back(attribIndex); + + // Update target node's input attributes + attribIndex.OwnerNodeIndex = destNode->Index; + attribIndex.AttributeIndex = static_cast(-(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, std::list::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> 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; + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.h b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.h new file mode 100644 index 00000000..1066fc7b --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.h @@ -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 +#include +// External Dependencies +#include +// Project Includes +#include "Resource/SHHandle.h" +#include "Editor/EditorWindow/SHEditorWindow.h" +#include "Animation/SHAnimationController.h" + +namespace SHADE +{ + /// + /// Editor for modifying the Animation Controller state machine. + /// + 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 Clip; + std::vector InputAttribs; + std::vector OutputAttribs; + std::vector Transitions; + bool EditingName = false; + }; + + struct LinkData + { + // Source/Dest Data + std::list::iterator SourceNode; + std::list::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; + + struct AnimControllerData + { + NodeIndex StartingNode = 0; + std::list Nodes; + std::unordered_map Params; + LinkMap Links; + int NextNodeIndex = 0; // Index to use for newly created nodes + std::unordered_map::iterator> IndexToNodeMap; + }; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + SHAnimationController controller; + std::optional controllerData; + // Persistent Cached Data + std::vector conditionsList; + std::vector 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::iterator createNode(AnimControllerData& data); + static LinkMap::iterator createLink(AnimControllerData& data, std::list::iterator sourceNode, std::list::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); + }; +} diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp index a6e506b5..b45f1e3c 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp @@ -22,7 +22,11 @@ #include "Assets/Asset Types/SHPrefabAsset.h" #include "Serialization/SHSerialization.h" #include + +#include "Assets/Asset Types/SHAnimClipContainerAsset.h" +#include "Assets/Asset Types/Models/SHModelAsset.h" #include "Serialization/Prefab/SHPrefabManager.h" +#include "Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h" namespace SHADE { @@ -367,11 +371,12 @@ namespace SHADE { switch (asset->type) { - case AssetType::INVALID: break; - case AssetType::SHADER: break; - case AssetType::SHADER_BUILT_IN: break; - case AssetType::TEXTURE: break; - case AssetType::MESH: break; + case AssetType::INVALID: break; + case AssetType::SHADER: break; + case AssetType::SHADER_BUILT_IN: break; + case AssetType::TEXTURE: break; + case AssetType::MODEL: break; + case AssetType::MESH: break; case AssetType::SCENE: { if(editor->LoadScene(asset->id)) @@ -382,7 +387,7 @@ namespace SHADE } } break; - case AssetType::PREFAB: break; + case AssetType::PREFAB: break; case AssetType::MATERIAL: if (auto matInspector = SHEditorWindowManager::GetEditorWindow()) { @@ -395,6 +400,12 @@ namespace SHADE scriptEngine->OpenFile(asset->path); } break; + case AssetType::ANIM_CONTAINER: + if (auto animInspector = SHEditorWindowManager::GetEditorWindow()) + { + animInspector->Open(asset->id); + } + break; case AssetType::MAX_COUNT: break; default:; } @@ -480,6 +491,48 @@ namespace SHADE isAssetBeingCreated = false; 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(model.id)}; + if (!data->anims.empty()) + { + const auto animContainerId = SHAssetManager::CreateNewAsset(AssetType::ANIM_CONTAINER, model.name + "Anims"); + auto data = SHAssetManager::GetData(animContainerId); + data->animRawDataAssetId = model.id; + SHAssetManager::SaveAsset(animContainerId); + if (auto animInspector = SHEditorWindowManager::GetEditorWindow()) + { + animInspector->Open(animContainerId); + } + QueueRefresh(); + isAssetBeingCreated = false; + ImGui::CloseCurrentPopup(); + } + } + + if (isSelected) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + } + ImGui::EndPopup(); } //if (ImGui::BeginMenu("Create Asset")) diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 8f0a8193..311b1cd0 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -454,7 +454,8 @@ namespace SHADE { if(auto entityTransform = SHComponentManager::GetComponent_s(eid)) { - editorCam->SetPosition(entityTransform->GetWorldPosition() + SHVec3(0.5f)); + SHVec3 scale = entityTransform->GetLocalScale(); + editorCam->SetPosition(entityTransform->GetWorldPosition() + scale * 0.5f); camSystem->CameraLookAt(*editorCam, entityTransform->GetWorldPosition()); camSystem->UpdateEditorCamera(SHFrameRateController::GetRawDeltaTime()); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index c655ff60..03a37a69 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -30,6 +30,7 @@ #include "../SHEditorWindowManager.h" #include "../AssetBrowser/SHAssetBrowser.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" +#include "Animation/SHAnimationClip.h" namespace SHADE { @@ -273,8 +274,8 @@ namespace SHADE } if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { - SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag"); - SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag"); + SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag", 0.1f, 0.0001f, std::numeric_limits::infinity()); + SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag", 0.1f, 0.0001f, std::numeric_limits::infinity()); SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "Interpolate"); @@ -377,7 +378,8 @@ namespace SHADE ( "Radius", [sphereShape] { return sphereShape->GetRelativeRadius(); }, - [sphereShape](float const& value) { sphereShape->SetRelativeRadius(value); } + [sphereShape](float const& value) { sphereShape->SetRelativeRadius(value); }, "Collider Radius", 0.1f, + 0.0001f, std::numeric_limits::infinity() ); } else if (collisionShape->GetType() == SHCollisionShape::Type::CAPSULE) @@ -388,13 +390,15 @@ namespace SHADE ( "Radius", [capsuleShape] { return capsuleShape->GetRelativeRadius(); }, - [capsuleShape](float const& value) { capsuleShape->SetRelativeRadius(value); } + [capsuleShape](float const& value) { capsuleShape->SetRelativeRadius(value); }, "Collider Radius", 0.1f, + 0.0001f, std::numeric_limits::infinity() ); SHEditorWidgets::DragFloat ( "Height", [capsuleShape] { return capsuleShape->GetRelativeHeight(); }, - [capsuleShape](float const& value) { capsuleShape->SetRelativeHeight(value); } + [capsuleShape](float const& value) { capsuleShape->SetRelativeHeight(value); }, "Collider Height", 0.1f, + 0.0001f, std::numeric_limits::infinity() ); } @@ -639,23 +643,29 @@ namespace SHADE if (ImGui::CollapsingHeader(componentType.get_name().data())) { DrawContextMenu(component); + + /* Animation Rig */ Handle const& rig = component->GetRig(); const auto RIG_NAME = rig ? SHResourceManager::GetAssetName(rig).value_or("") : ""; - SHEditorWidgets::DragDropReadOnlyField("Rig", RIG_NAME, [component]() - { - Handle const& rig = component->GetRig(); - return SHResourceManager::GetAssetID(rig).value_or(0); - }, - [component](AssetID const& id) - { - if (SHAssetManager::GetType(id) != AssetType::MODEL) + SHEditorWidgets::DragDropReadOnlyField + ( + "Rig", RIG_NAME, [component]() { - SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") - return; - } - component->SetRig(SHResourceManager::LoadOrGet(id)); - SHResourceManager::FinaliseChanges(); - }, SHDragDrop::DRAG_RESOURCE); + Handle const& rig = component->GetRig(); + return SHResourceManager::GetAssetID(rig).value_or(0); + }, + [component](AssetID const& id) + { + if (SHAssetManager::GetType(id) != AssetType::MODEL) + { + SHLOG_WARNING("Attempted to assign non mesh rig asset to Animator Rig property!") + return; + } + component->SetRig(SHResourceManager::LoadOrGet(id)); + SHResourceManager::FinaliseChanges(); + }, + SHDragDrop::DRAG_RESOURCE + ); if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { if (Handle const& rig = component->GetRig()) @@ -664,28 +674,34 @@ namespace SHADE SHEditorWindowManager::GetEditorWindow()->SetScrollTo(assetID); } } - Handle const& clip = component->GetCurrentClip(); - const auto CLIP_NAME = clip ? SHResourceManager::GetAssetName(clip).value_or("") : ""; - SHEditorWidgets::DragDropReadOnlyField("Clip", CLIP_NAME, - [component]() - { - Handle const& clip = component->GetCurrentClip(); - return SHResourceManager::GetAssetID(clip).value_or(0); - }, - [component](AssetID const& id) - { - if (SHAssetManager::GetType(id) != AssetType::MODEL) + + /* Animation Controller */ + Handle animController = component->GetAnimationController(); + const auto AC_NAME = animController ? SHResourceManager::GetAssetName(animController).value_or("") : ""; + SHEditorWidgets::DragDropReadOnlyField + ( + "Animation Controller", AC_NAME, [component]() { - SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") - return; - } - component->SetClip(SHResourceManager::LoadOrGet(id)); - }, SHDragDrop::DRAG_RESOURCE); + Handle ac = component->GetAnimationController(); + return SHResourceManager::GetAssetID(ac).value_or(0); + }, + [component](AssetID const& id) + { + if (SHAssetManager::GetType(id) != AssetType::ANIM_CONTROLLER) + { + SHLOG_WARNING("Attempted to assign non animation controller asset to Animator Animation Controller property!") + return; + } + component->SetAnimationController(SHResourceManager::LoadOrGet(id)); + SHResourceManager::FinaliseChanges(); + }, + SHDragDrop::DRAG_RESOURCE + ); if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - if (Handle const& clip = component->GetCurrentClip()) + if (Handle ac = component->GetAnimationController()) { - AssetID assetID = SHResourceManager::GetAssetID(clip).value_or(0); + AssetID assetID = SHResourceManager::GetAssetID(ac).value_or(0); SHEditorWindowManager::GetEditorWindow()->SetScrollTo(assetID); } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index faede2bd..f331f499 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -26,6 +26,8 @@ #include "SHEditorComponentView.h" #include "AudioSystem/SHAudioListenerComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" +#include "Camera/SHCameraSystem.h" +#include "FRC/SHFramerateController.h" namespace SHADE { @@ -106,6 +108,7 @@ namespace SHADE { if (editor && !editor->selectedEntities.empty()) { + DrawMenuBar(); EntityID const& eid = editor->selectedEntities[0]; SHEntity* entity = SHEntityManager::GetEntityByID(eid); SHSceneNode* entityNode = SHSceneManager::GetCurrentSceneGraph().GetNode(eid); @@ -114,6 +117,7 @@ namespace SHADE ImGui::End(); return; } + ImGui::TextColored(ImGuiColors::green, "EID: %zu", eid); SHEditorWidgets::CheckBox("##IsActive", [entityNode]()->bool {return entityNode->IsActive(); }, [entityNode](bool const& active) {entityNode->SetActive(active); }); ImGui::SameLine(); @@ -222,4 +226,36 @@ namespace SHADE { SHEditorWindow::Exit(); } + void SHEditorInspector::DrawMenuBar() + { + if(ImGui::BeginMenuBar()) + { + EntityID const& eid = editor->selectedEntities[0]; + SHEntity* entity = SHEntityManager::GetEntityByID(eid); + SHSceneNode* entityNode = SHSceneManager::GetCurrentSceneGraph().GetNode(eid); + if (!entity || !entityNode) + { + ImGui::EndMenuBar(); + return; + } + if (ImGui::SmallButton("Look At")) + { + editor->selectedEntities.clear(); + editor->selectedEntities.push_back(eid); + if (auto camSystem = SHSystemManager::GetSystem()) + { + if (auto editorCam = camSystem->GetEditorCamera()) + { + if (auto entityTransform = SHComponentManager::GetComponent_s(eid)) + { + editorCam->SetPosition(entityTransform->GetWorldPosition() + SHVec3(0.5f)); + camSystem->CameraLookAt(*editorCam, entityTransform->GetWorldPosition()); + camSystem->UpdateEditorCamera(SHFrameRateController::GetRawDeltaTime()); + } + } + } + } + ImGui::EndMenuBar(); + } + } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.h b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.h index 06676beb..431f3239 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.h +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.h @@ -25,6 +25,6 @@ namespace SHADE void Exit() override; private: - + void DrawMenuBar(); }; } diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index 21cc85f4..65e497aa 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -358,6 +358,7 @@ namespace SHADE void SHEditorMenuBar::DrawPhysicsSettings() noexcept { + ImGui::SetNextWindowSize({400.0f, 0.0f}); if (ImGui::BeginMenu("Physics Settings")) { if (auto* physicsSystem = SHSystemManager::GetSystem()) diff --git a/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.cpp new file mode 100644 index 00000000..563c0f60 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.cpp @@ -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 +// External Dependencies +#include +#include +// 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 rawAnim, + std::function 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(NEW_ASSET_ID); + animClip->name = newAssetName; + animClip->firstIndex = firstIndex; + animClip->lastIndex = lastIndex; + animClip->animRawDataAssetId = SHResourceManager::GetAssetID(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(); + } + + 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(); + prompt->Init(containerAsset, container, currRawAnim, [this](AssetID createdAssetId) + { + if (createdAssetId != INVALID_ASSET_ID) + { + childAnimClips.emplace_back(SHResourceManager::LoadOrGet(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 animClipName = SHResourceManager::GetAssetName(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(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(assetId); + + // Load anim clips + if (container) + { + currRawAnim = SHResourceManager::LoadOrGet(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(currRawAnim).value_or("Unnamed Asset"); + ImGui::Text(ASSET_NAME.c_str()); + + ImGui::EndMenuBar(); + } + } + + std::vector> SHRawAnimInspector::getChildAnimClips(AssetID containerId) + { + auto const containerAsset {*SHAssetManager::GetAsset(containerId)}; + std::vector> animClips; + + for (auto const& asset : containerAsset.subAssets) + { + animClips.emplace_back(SHResourceManager::LoadOrGet(asset->id)); + } + + return animClips; + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h new file mode 100644 index 00000000..6790cded --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/RawAnimationInspector/SHRawAnimInspector.h @@ -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 */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Prompt for creating an animation clip. Init() must be called to pass in the correct + /// SHRawAnimation that the created clip will use. + /// + class SHAnimClipCreatePrompt : public SHPopUpWindow + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors/Destructors */ + /*---------------------------------------------------------------------------------*/ + SHAnimClipCreatePrompt(); + + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + void Init( + SHAsset* contAsset, + SHAnimClipContainerAsset* cont, + Handle rawAnim, + std::function onClose = nullptr + ); + void Draw() override; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + std::string newAssetName; + uint32_t firstIndex = 0; + uint32_t lastIndex = 0; + Handle rawAnimation; + SHAsset* containerAsset{nullptr}; + SHAnimClipContainerAsset* container{nullptr}; + std::function onClose; + }; + + /// + /// Editor for generating SHAnimationClips from a single SHRawAnimation object. + /// + 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 currRawAnim; + std::vector> childAnimClips; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void drawMenuBar(); + std::vector> getChildAnimClips(AssetID containerId); + }; +} diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h index 290ed622..e0247d69 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h @@ -1,10 +1,12 @@ #pragma once -#include "MenuBar/SHEditorMenuBar.h" //Menu Bar -#include "HierarchyPanel/SHHierarchyPanel.h" //Hierarchy Panel -#include "Inspector/SHEditorInspector.h" //Inspector -#include "Profiling/SHEditorProfiler.h" //Profiler -#include "ViewportWindow/SHEditorViewport.h" //Editor Viewport -#include "AssetBrowser/SHAssetBrowser.h" //Asset Browser -#include "MaterialInspector/SHMaterialInspector.h" //Material Inspector -#include "ColliderTagPanel/SHColliderTagPanel.h" //Collider Tag Panel -#include "InputBindings/SHInputBindingsPanel.h" //Input Bindings \ No newline at end of file +#include "MenuBar/SHEditorMenuBar.h" // Menu Bar +#include "HierarchyPanel/SHHierarchyPanel.h" // Hierarchy Panel +#include "Inspector/SHEditorInspector.h" // Inspector +#include "Profiling/SHEditorProfiler.h" // Profiler +#include "ViewportWindow/SHEditorViewport.h" // Editor Viewport +#include "AssetBrowser/SHAssetBrowser.h" // Asset Browser +#include "MaterialInspector/SHMaterialInspector.h" // Material Inspector +#include "ColliderTagPanel/SHColliderTagPanel.h" // Collider Tag Panel +#include "InputBindings/SHInputBindingsPanel.h" // Input Bindings +#include "EditorWindow/Animation/SHAnimationControllerEditor.h" // Animation Controller Editor +#include "EditorWindow/RawAnimationInspector/SHRawAnimInspector.h" // Raw Animation Inspector diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 4c7db57c..268af73e 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -39,6 +39,7 @@ //|| Library Includes || //#==============================================================# #include +#include #include #include #include @@ -97,6 +98,10 @@ namespace SHADE SHLOG_CRITICAL("Failed to create ImGui Context") } } + if (ImNodes::CreateContext() == nullptr) + { + SHLOG_CRITICAL("Failed to create ImNodes Context") + } #ifdef SHEDITOR editorConfig = &SHConfigurationManager::LoadEditorConfig(); @@ -113,6 +118,8 @@ namespace SHADE SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); //Add popup windows SHEditorWindowManager::CreatePopupWindow(); @@ -340,6 +347,7 @@ namespace SHADE { window->Init(); } + ImNodes::DestroyContext(); ImGui_ImplVulkan_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); @@ -669,6 +677,12 @@ namespace SHADE SaveScene(); } } + if(editorState == State::PLAY && ImGui::IsKeyReleased(ImGuiKey_LeftAlt)) + { + SHInputManager::SetMouseCentering(!SHInputManager::GetMouseCentering()); + SHWindow::SetMouseVisible(!SHWindow::GetMouseVisible()); + + } if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z)) { SHCommandManager::RedoCommand(); diff --git a/SHADE_Engine/src/Editor/SHEditorUI.cpp b/SHADE_Engine/src/Editor/SHEditorUI.cpp index 45cf0f6c..212a87ae 100644 --- a/SHADE_Engine/src/Editor/SHEditorUI.cpp +++ b/SHADE_Engine/src/Editor/SHEditorUI.cpp @@ -137,6 +137,15 @@ namespace SHADE { 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) { return ImGui::SmallButton(title.c_str()); @@ -369,9 +378,9 @@ namespace SHADE // Attempt to get the asset's data for rendering editor auto asset = SHAssetManager::GetAsset(value); std::string assetName; - if (asset.has_value()) + if (asset) { - assetName = asset.value().name; + assetName = asset->name; } // Editor @@ -382,9 +391,9 @@ namespace SHADE { // Check if type matches 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; } SHDragDrop::EndTarget(); diff --git a/SHADE_Engine/src/Editor/SHEditorUI.h b/SHADE_Engine/src/Editor/SHEditorUI.h index 23cc2d1a..8d4a7b6e 100644 --- a/SHADE_Engine/src/Editor/SHEditorUI.h +++ b/SHADE_Engine/src/Editor/SHEditorUI.h @@ -90,7 +90,7 @@ namespace SHADE /// True if the header is open, false otherwise. static bool CollapsingHeader(const std::string& title, bool* isHovered = nullptr); static void SameLine(); - static void Separator(); + static void Separator(); /*-----------------------------------------------------------------------------*/ /* ImGui Wrapper Functions - Queries */ @@ -98,9 +98,9 @@ namespace SHADE static bool IsItemHovered(); /*-----------------------------------------------------------------------------*/ - /* ImGui Wrapper Functions - Menu */ - /*-----------------------------------------------------------------------------*/ - static bool BeginMenu(const std::string& label); + /* ImGui Wrapper Functions - Menu */ + /*-----------------------------------------------------------------------------*/ + static bool BeginMenu(const std::string& label); static bool BeginMenu(const std::string& label, const char* icon); static void EndMenu(); static void BeginTooltip(); @@ -150,6 +150,12 @@ namespace SHADE /// Text to display. static void Text(const std::string& title); /// + /// Renders a text widget that is vertically and horizontally centered in the current + /// window. + /// + /// Text to display. + static void CenteredText(const std::string& text); + /// /// Creates a small inline button widget. ///
/// Wraps up ImGui::SmallButton(). @@ -164,8 +170,8 @@ namespace SHADE ///
/// Text to display. /// True if button was pressed. - static bool Button(const std::string& title); - static bool Selectable(const std::string& label); + static bool Button(const std::string& title); + static bool Selectable(const std::string& label); static bool Selectable(const std::string& label, const char* icon); /// /// Creates a checkbox widget for boolean input. diff --git a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp index 2698e3f2..124bbfb3 100644 --- a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp +++ b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp @@ -418,12 +418,16 @@ namespace SHADE } template - static bool DragDropReadOnlyField(std::string const& label, std::string_view const& fieldVTextValue, std::function const& get, std::function 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 const& get, std::function const& set, SHDragDrop::DragDropTag const& dragDropTag, std::string_view const& tooltip = {}, float customWidth = -1.0f) { std::string text = fieldVTextValue.data(); ImGui::BeginGroup(); ImGui::PushID(label.data()); TextLabel(label); + if (customWidth > 0.0f) + { + ImGui::SetNextItemWidth(customWidth); + } bool changed = ImGui::InputText("##inputText", &text, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_AutoSelectAll, nullptr, nullptr); if(SHDragDrop::BeginTarget()) { diff --git a/SHADE_Engine/src/Resource/SHResourceManager.cpp b/SHADE_Engine/src/Resource/SHResourceManager.cpp index 9ddb8814..1875ee46 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.cpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.cpp @@ -127,8 +127,8 @@ namespace SHADE if (assetId.has_value()) { const auto ASSET_INFO = SHAssetManager::GetAsset(assetId.value()); - if (ASSET_INFO.has_value()) - return ASSET_INFO.value().name; + if (ASSET_INFO) + return ASSET_INFO->name; } return {}; } diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index 4188bde9..2dfc7dac 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -21,6 +21,8 @@ of DigiPen Institute of Technology is prohibited. #include "Assets/Asset Types/Models/SHModelAsset.h" #include "Assets/Asset Types/SHTextureAsset.h" #include "Assets/Asset Types/SHShaderAsset.h" +#include "Assets/Asset Types/SHAnimClipContainerAsset.h" +#include "Assets/Asset Types/SHAnimControllerAsset.h" #include "Graphics/Shaders/SHVkShaderModule.h" #include "Graphics/MiddleEnd/Textures/SHTextureLibrary.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 "Assets/Asset Types/SHMaterialAsset.h" #include "Graphics/MiddleEnd/TextRendering/SHFont.h" -#include "Animation/SHAnimationClip.h" +#include "Animation/SHRawAnimation.h" #include "Animation/SHRig.h" namespace SHADE @@ -38,6 +40,8 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ class SHMaterial; struct SHRigNode; + class SHAnimationClip; + class SHAnimationController; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -46,15 +50,17 @@ namespace SHADE /// Template structs that maps a resource to their loaded asset representation type. /// template - struct SHResourceLoader { using AssetType = void; }; - template<> struct SHResourceLoader { using AssetType = SHMeshAsset; }; - template<> struct SHResourceLoader { using AssetType = SHTextureAsset; }; - template<> struct SHResourceLoader { using AssetType = SHShaderAsset; }; - template<> struct SHResourceLoader { using AssetType = SHMaterialAsset; }; - template<> struct SHResourceLoader { using AssetType = SHMaterialSpec; }; - template<> struct SHResourceLoader { using AssetType = SHFontAsset; }; - template<> struct SHResourceLoader { using AssetType = SHModelAsset; }; - template<> struct SHResourceLoader { using AssetType = SHModelAsset; }; + struct SHResourceLoader { using AssetType = void; }; + template<> struct SHResourceLoader { using AssetType = SHMeshAsset; }; + template<> struct SHResourceLoader { using AssetType = SHTextureAsset; }; + template<> struct SHResourceLoader { using AssetType = SHShaderAsset; }; + template<> struct SHResourceLoader { using AssetType = SHMaterialAsset; }; + template<> struct SHResourceLoader { using AssetType = SHMaterialSpec; }; + template<> struct SHResourceLoader { using AssetType = SHFontAsset; }; + template<> struct SHResourceLoader { using AssetType = SHModelAsset; }; + template<> struct SHResourceLoader { using AssetType = SHModelAsset; }; + template<> struct SHResourceLoader { using AssetType = SHAnimClipAsset; }; + template<> struct SHResourceLoader { using AssetType = SHAnimControllerAsset; }; /// /// Static class responsible for loading and caching runtime resources from their diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 231615a5..6474b478 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -25,6 +25,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Serialization/SHYAMLConverters.h" +#include "Animation/SHAnimationClip.h" namespace SHADE { @@ -35,14 +36,16 @@ namespace SHADE Handle SHResourceManager::LoadOrGet(AssetID assetId) { // Check if it is an unsupported type - if (!std::is_same_v && - !std::is_same_v && - !std::is_same_v && - !std::is_same_v && - !std::is_same_v && - !std::is_same_v && - !std::is_same_v && - !std::is_same_v + if (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v ) { static_assert(true, "Unsupported Resource Type specified for SHResourceManager."); @@ -159,8 +162,8 @@ namespace SHADE if (assetId.has_value()) { const auto ASSET_INFO = SHAssetManager::GetAsset(assetId.value()); - if (ASSET_INFO.has_value()) - return ASSET_INFO.value().name; + if (ASSET_INFO) + return ASSET_INFO->name; } return {}; } @@ -355,10 +358,30 @@ namespace SHADE loadedAssetData.emplace_back(assetId); return resourceHub.Create(assetData.rig, rigNodeStore); } + else if constexpr (std::is_same_v) + { + loadedAssetData.emplace_back(assetId); + if (assetData.anims.empty()) + return {}; + return resourceHub.Create(*assetData.anims[0]); + } else if constexpr (std::is_same_v) { loadedAssetData.emplace_back(assetId); - return resourceHub.Create(*assetData.anims[0]); + return resourceHub.Create + ( + LoadOrGet(assetData.animRawDataAssetId), + assetData.firstIndex, + assetData.lastIndex + ); + } + else if constexpr (std::is_same_v) + { + loadedAssetData.emplace_back(assetId); + return resourceHub.Create + ( + // TODO + ); } } } diff --git a/SHADE_Engine/src/Resource/SHResourceManagerInterface.cpp b/SHADE_Engine/src/Resource/SHResourceManagerInterface.cpp index d89a7b16..8feef560 100644 --- a/SHADE_Engine/src/Resource/SHResourceManagerInterface.cpp +++ b/SHADE_Engine/src/Resource/SHResourceManagerInterface.cpp @@ -44,6 +44,21 @@ namespace SHADE return SHResourceManager::LoadOrGet(assetId); } + Handle SHResourceManagerInterface::LoadOrGetAnimationClip(AssetID assetId) + { + return SHResourceManager::LoadOrGet(assetId); + } + + Handle SHResourceManagerInterface::LoadOrGetAnimationController(AssetID assetId) + { + return SHResourceManager::LoadOrGet(assetId); + } + + Handle SHResourceManagerInterface::LoadOrGetRig(AssetID assetId) + { + return SHResourceManager::LoadOrGet(assetId); + } + /*-----------------------------------------------------------------------------------*/ /* Query Functions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHResourceManagerInterface.h b/SHADE_Engine/src/Resource/SHResourceManagerInterface.h index 359bd7c8..a09f3463 100644 --- a/SHADE_Engine/src/Resource/SHResourceManagerInterface.h +++ b/SHADE_Engine/src/Resource/SHResourceManagerInterface.h @@ -29,6 +29,9 @@ namespace SHADE struct SHMaterialSpec; class SHMaterial; class SHFont; + class SHAnimationClip; + class SHRig; + class SHAnimationController; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -80,6 +83,24 @@ namespace SHADE /// Asset ID of the resource to load. /// Handle to the resource to retrieve. static Handle LoadOrGetFont(AssetID assetId); + /// + /// Wrapper for SHResourceManager::LoadOrGet(). + /// + /// Asset ID of the resource to load. + /// Handle to the resource to retrieve. + static Handle LoadOrGetAnimationClip(AssetID assetId); + /// + /// Wrapper for SHResourceManager::LoadOrGet(). + /// + /// Asset ID of the resource to load. + /// Handle to the resource to retrieve. + static Handle LoadOrGetAnimationController(AssetID assetId); + /// + /// Wrapper for SHResourceManager::LoadOrGet(). + /// + /// Asset ID of the resource to load. + /// Handle to the resource to retrieve. + static Handle LoadOrGetRig(AssetID assetId); /*---------------------------------------------------------------------------------*/ /* Query Functions */ diff --git a/SHADE_Engine/src/Scripting/SHVSUtilities.cpp b/SHADE_Engine/src/Scripting/SHVSUtilities.cpp index 17435d6f..d34e0211 100644 --- a/SHADE_Engine/src/Scripting/SHVSUtilities.cpp +++ b/SHADE_Engine/src/Scripting/SHVSUtilities.cpp @@ -115,7 +115,7 @@ namespace SHADE #else 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) { SHLOG_ERROR("[SHVSUtilities] Unable to get path to Visual Studio installation. SHVSUtilities will not work."); diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index b1ae92e0..0bf66a16 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -428,13 +428,13 @@ namespace YAML struct convert { static constexpr std::string_view RIG_YAML_TAG = "Rig"; - static constexpr std::string_view CLIP_YAML_TAG = "Clip"; + static constexpr std::string_view AC_YAML_TAG = "AnimationController"; static YAML::Node encode(SHAnimatorComponent const& rhs) { YAML::Node node; node[RIG_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetRig()).value_or(0); - node[CLIP_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetCurrentClip()).value_or(0); + node[AC_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetAnimationController()).value_or(0); return node; } static bool decode(YAML::Node const& node, SHAnimatorComponent& rhs) @@ -443,9 +443,9 @@ namespace YAML { rhs.SetRig(SHResourceManager::LoadOrGet(node[RIG_YAML_TAG.data()].as())); } - if (node[CLIP_YAML_TAG.data()].IsDefined()) + if (node[AC_YAML_TAG.data()].IsDefined()) { - rhs.SetClip(SHResourceManager::LoadOrGet(node[CLIP_YAML_TAG.data()].as())); + rhs.SetAnimationController(SHResourceManager::LoadOrGet(node[AC_YAML_TAG.data()].as())); } return true; } diff --git a/SHADE_Managed/src/Assets/AnimationClipAsset.cxx b/SHADE_Managed/src/Assets/AnimationClipAsset.cxx new file mode 100644 index 00000000..603d3983 --- /dev/null +++ b/SHADE_Managed/src/Assets/AnimationClipAsset.cxx @@ -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 AnimationClipAsset::NativeObject::get() + try + { + return SHResourceManagerInterface::LoadOrGetAnimationClip(asset.NativeAssetID); + } + catch (const BadHandleCastException&) + { + return Handle(); + } + 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); + } +} diff --git a/SHADE_Managed/src/Assets/AnimationClipAsset.hxx b/SHADE_Managed/src/Assets/AnimationClipAsset.hxx new file mode 100644 index 00000000..5a8c7845 --- /dev/null +++ b/SHADE_Managed/src/Assets/AnimationClipAsset.hxx @@ -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 +{ + /// + /// 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. + /// + public value struct AnimationClipAsset + { + internal: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Copy of the Handle to the native object. + /// + property Handle NativeObject + { + Handle get(); + } + /// + /// The raw asset ID of the asset. + /// + property AssetID NativeAssetID + { + AssetID get(); + } + + /*-----------------------------------------------------------------------------*/ + /* Constructors/Destructor */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for the AnimationClip. + /// + /// AssetID to the AnimationClip asset. + AnimationClipAsset(AssetID AnimationClipId); + + /*-----------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------*/ + /// + /// Implicit conversion operator to enable checking if a AnimationClip is valid. + /// + /// Asset to check. + /// True if the Asset is valid. + static operator bool(AnimationClipAsset asset); + + /*-----------------------------------------------------------------------------*/ + /* Conversion Operators */ + /*-----------------------------------------------------------------------------*/ + /// + /// Conversion operator to enable casting from a AnimationClip to an Asset. + /// + /// Vector3 to convert from. + static explicit operator Asset(AnimationClipAsset nativeAsset); + /// + /// Conversion operator to enable casting from a Asset to a AnimationClip. + /// + /// + static explicit operator AnimationClipAsset(Asset asset); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + Asset asset; + }; +} diff --git a/SHADE_Managed/src/Assets/AnimationControllerAsset.cxx b/SHADE_Managed/src/Assets/AnimationControllerAsset.cxx new file mode 100644 index 00000000..044f94d2 --- /dev/null +++ b/SHADE_Managed/src/Assets/AnimationControllerAsset.cxx @@ -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 AnimationControllerAsset::NativeObject::get() + try + { + return SHResourceManagerInterface::LoadOrGetAnimationController(asset.NativeAssetID); + } + catch (const BadHandleCastException&) + { + return Handle(); + } + 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); + } +} diff --git a/SHADE_Managed/src/Assets/AnimationControllerAsset.hxx b/SHADE_Managed/src/Assets/AnimationControllerAsset.hxx new file mode 100644 index 00000000..1be74fb9 --- /dev/null +++ b/SHADE_Managed/src/Assets/AnimationControllerAsset.hxx @@ -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 +{ + /// + /// Managed counterpart of the native AnimationController object containing the + /// state machine for controlling what AnimationClips that an Animator should play. + /// + public value struct AnimationControllerAsset + { + internal: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Copy of the Handle to the native object. + /// + property Handle NativeObject + { + Handle get(); + } + /// + /// The raw asset ID of the asset. + /// + property AssetID NativeAssetID + { + AssetID get(); + } + + /*-----------------------------------------------------------------------------*/ + /* Constructors/Destructor */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for the AnimationController. + /// + /// AssetID to the AnimationController asset. + AnimationControllerAsset(AssetID AnimationControllerId); + + /*-----------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------*/ + /// + /// Implicit conversion operator to enable checking if a AnimationController is valid. + /// + /// Asset to check. + /// True if the Asset is valid. + static operator bool(AnimationControllerAsset asset); + + /*-----------------------------------------------------------------------------*/ + /* Conversion Operators */ + /*-----------------------------------------------------------------------------*/ + /// + /// Conversion operator to enable casting from a AnimationController to an Asset. + /// + /// Vector3 to convert from. + static explicit operator Asset(AnimationControllerAsset nativeAsset); + /// + /// Conversion operator to enable casting from a Asset to a AnimationController. + /// + /// + static explicit operator AnimationControllerAsset(Asset asset); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + Asset asset; + }; +} diff --git a/SHADE_Managed/src/Assets/AnimationRigAsset.cxx b/SHADE_Managed/src/Assets/AnimationRigAsset.cxx new file mode 100644 index 00000000..154c6aee --- /dev/null +++ b/SHADE_Managed/src/Assets/AnimationRigAsset.cxx @@ -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 AnimationRigAsset::NativeObject::get() + try + { + return SHResourceManagerInterface::LoadOrGetRig(asset.NativeAssetID); + } + catch (const BadHandleCastException&) + { + return Handle(); + } + 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); + } +} diff --git a/SHADE_Managed/src/Assets/AnimationRigAsset.hxx b/SHADE_Managed/src/Assets/AnimationRigAsset.hxx new file mode 100644 index 00000000..be43b7f0 --- /dev/null +++ b/SHADE_Managed/src/Assets/AnimationRigAsset.hxx @@ -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 +{ + /// + /// Managed counterpart of the native Animation Rig object that specifies how an + /// Animation Clip affects the model that this Rig is attached to. + /// + public value struct AnimationRigAsset + { + internal: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Copy of the Handle to the native object. + /// + property Handle NativeObject + { + Handle get(); + } + /// + /// The raw asset ID of the asset. + /// + property AssetID NativeAssetID + { + AssetID get(); + } + + /*-----------------------------------------------------------------------------*/ + /* Constructors/Destructor */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for the AnimationRig. + /// + /// AssetID to the AnimationRig asset. + AnimationRigAsset(AssetID AnimationRigId); + + /*-----------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------*/ + /// + /// Implicit conversion operator to enable checking if a AnimationRig is valid. + /// + /// Asset to check. + /// True if the Asset is valid. + static operator bool(AnimationRigAsset asset); + + /*-----------------------------------------------------------------------------*/ + /* Conversion Operators */ + /*-----------------------------------------------------------------------------*/ + /// + /// Conversion operator to enable casting from a AnimationRig to an Asset. + /// + /// Vector3 to convert from. + static explicit operator Asset(AnimationRigAsset nativeAsset); + /// + /// Conversion operator to enable casting from a Asset to a AnimationRig. + /// + /// + static explicit operator AnimationRigAsset(Asset asset); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + Asset asset; + }; +} diff --git a/SHADE_Managed/src/Components/Animator.cxx b/SHADE_Managed/src/Components/Animator.cxx new file mode 100644 index 00000000..364ce046 --- /dev/null +++ b/SHADE_Managed/src/Components/Animator.cxx @@ -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()); + } + 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()); + } + else + { + GetNativeComponent()->SetRig(Handle(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 + void Animator::SetParameter(System::String^ paramName, T value) + { + if (T::typeid == int::typeid) + { + GetNativeComponent()->SetParameter(Convert::ToNative(paramName), static_cast(value)); + } + else if (T::typeid == float::typeid) + { + GetNativeComponent()->SetParameter(Convert::ToNative(paramName), static_cast(value)); + } + else if (T::typeid == bool::typeid) + { + GetNativeComponent()->SetParameter(Convert::ToNative(paramName), static_cast(value)); + } + } + + void Animator::SetTrigger(System::String^ paramName) + { + GetNativeComponent()->SetTrigger(Convert::ToNative(paramName)); + } + + System::Nullable Animator::GetIntParameter(System::String^ paramName) + { + auto val = GetNativeComponent()->GetParameter(Convert::ToNative(paramName)); + if (val.has_value()) + return System::Nullable(val.value()); + return {}; + } + + System::Nullable Animator::GetFloatParameter(System::String^ paramName) + { + auto val = GetNativeComponent()->GetParameter(Convert::ToNative(paramName)); + if (val.has_value()) + return System::Nullable(val.value()); + return {}; + } + + System::Nullable Animator::GetBoolParameter(System::String^ paramName) + { + auto val = GetNativeComponent()->GetParameter(Convert::ToNative(paramName)); + if (val.has_value()) + return System::Nullable(val.value()); + return {}; + } + + System::Nullable Animator::GetTriggerState(System::String^ paramName) + { + return GetBoolParameter(paramName); + } +} diff --git a/SHADE_Managed/src/Components/Animator.hxx b/SHADE_Managed/src/Components/Animator.hxx new file mode 100644 index 00000000..7100c54b --- /dev/null +++ b/SHADE_Managed/src/Components/Animator.hxx @@ -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 +{ + /// + /// CLR version of the SHADE Engine's SHAnimatorComponent. + /// + public ref class Animator : public Component + { + internal: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructs a Animator Component that represents a native Animator + /// component tied to the specified Entity. + /// + /// Entity that this Component will be tied to. + Animator(Entity entity); + + public: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Animation Controller used to controller the animation of this Animator. + /// + property AnimationControllerAsset AnimationController + { + AnimationControllerAsset get(); + void set(AnimationControllerAsset value); + } + /// + /// The shared Material used to render this Animator and other Animators + /// using the same base Material. + /// + property AnimationRigAsset Rig + { + AnimationRigAsset get(); + void set(AnimationRigAsset value); + } + /// + /// Name of the current node if there is an animation controller attached. If + /// there is none, null is returned. + /// + property System::String^ CurrentNodeName + { + System::String^ get(); + } + + /*-----------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Plays the currently loaded animation from the last time. + /// + void Play(); + /// + /// Plays the specified animation clip from the start. This will unset any + /// SHAnimationControllers that have been set. + /// + /// Animation clip to play. + void Play(AnimationClipAsset clip); + /// + /// Plays the specified animation clip from the start one time only. This will unset + /// any SHAnimationControllers that have been set. + /// + /// Animation clip to play. + void PlayOneShot(AnimationClipAsset clip); + /// + /// Plays the currently loaded animation clip from the start. Note that this only + /// works when using manual playback mode. + /// + void PlayFromStart(); + /// + /// Pauses the animation at the current time. + /// + void Pause(); + /// + /// 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. + /// + void Stop(); + + /*-----------------------------------------------------------------------------*/ + /* Parameter Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// 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. + /// + /// + /// Type of parameter. Only bool, int, floats are supported. + /// + /// Name of the parameter. + /// Value to set the parameter to. + generic + void SetParameter(System::String^ paramName, T value); + /// + /// 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. + /// + /// Name of the parameter. + void SetTrigger(System::String^ paramName); + /// + /// 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. + /// + /// Name of the parameter. + /// The value of the parameter or nothing if invalid. + System::Nullable GetIntParameter(System::String^ paramName); + /// + /// 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. + /// + /// Name of the parameter. + /// The value of the parameter or nothing if invalid. + System::Nullable GetFloatParameter(System::String^ paramName); + /// + /// 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. + /// + /// Name of the parameter. + /// The value of the parameter or nothing if invalid. + System::Nullable GetBoolParameter(System::String^ paramName); + /// + /// 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. + /// + /// Name of the parameter. + /// True if the trigger is set. + System::Nullable GetTriggerState(System::String^ paramName); + }; +} + diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index ac191cdd..fcabfb0a 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -16,6 +16,7 @@ of DigiPen Institute of Technology is prohibited. #include "Collider.hxx" #include "Physics/Collision/Shapes/SHBox.h" +#include "Physics/Collision/Shapes/SHCapsule.h" #include "Physics/Collision/Shapes/SHSphere.h" #include "Utility/Debug.hxx" @@ -118,6 +119,29 @@ namespace SHADE } } + /*---------------------------------------------------------------------------------*/ + /* SphereCollider - Properties */ + /*---------------------------------------------------------------------------------*/ + Vector3 SphereCollider::Center::get() + { + return Convert::ToCLI(getNativeCollisionShape().GetWorldCentroid()); + } + float SphereCollider::Radius::get() + { + return getNativeCollisionShape().GetWorldRadius(); + } + void SphereCollider::Radius::set(float value) + { + getNativeCollisionShape().SetWorldRadius(value); + } + + /*---------------------------------------------------------------------------------*/ + /* SphereCollider - Constructors */ + /*---------------------------------------------------------------------------------*/ + SphereCollider::SphereCollider(int arrayIndex, Entity attachedEntity) + : CollisionShape{ arrayIndex, attachedEntity } + {} + /*---------------------------------------------------------------------------------*/ /* BoxCollider - Constructors */ /*---------------------------------------------------------------------------------*/ @@ -147,53 +171,33 @@ namespace SHADE } /*---------------------------------------------------------------------------------*/ - /* BoxCollider - Usage Functions */ + /* CapsuleCollider - Properties */ /*---------------------------------------------------------------------------------*/ - bool BoxCollider::TestPoint(Vector3 point) + Vector3 CapsuleCollider::Center::get() { - //return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); - return false; + return Convert::ToCLI(getNativeCollisionShape().GetWorldCentroid()); } - bool BoxCollider::Raycast(Ray ray, float maxDistance) + float CapsuleCollider::Radius::get() { - //return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); - return false; + return getNativeCollisionShape().GetWorldRadius(); + } + void CapsuleCollider::Radius::set(float value) + { + getNativeCollisionShape().SetWorldRadius(value); + } + float CapsuleCollider::Height::get() + { + return getNativeCollisionShape().GetWorldHeight(); + } + void CapsuleCollider::Height::set(float value) + { + getNativeCollisionShape().SetWorldHeight(value); } /*---------------------------------------------------------------------------------*/ - /* SphereCollider - Properties */ + /* CapsuleCollider - Constructors */ /*---------------------------------------------------------------------------------*/ - Vector3 SphereCollider::Center::get() - { - return Convert::ToCLI(getNativeCollisionShape().GetWorldCentroid()); - } - float SphereCollider::Radius::get() - { - return getNativeCollisionShape().GetWorldRadius(); - } - void SphereCollider::Radius::set(float value) - { - getNativeCollisionShape().SetWorldRadius(value); - } - - /*---------------------------------------------------------------------------------*/ - /* SphereCollider - Usage Functions */ - /*---------------------------------------------------------------------------------*/ - bool SphereCollider::TestPoint(Vector3 point) - { - //return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); - return false; - } - bool SphereCollider::Raycast(Ray ray, float maxDistance) - { - //return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); - return false; - } - - /*---------------------------------------------------------------------------------*/ - /* SphereCollider - Constructors */ - /*---------------------------------------------------------------------------------*/ - SphereCollider::SphereCollider(int arrayIndex, Entity attachedEntity) + CapsuleCollider::CapsuleCollider(int arrayIndex, Entity attachedEntity) : CollisionShape{ arrayIndex, attachedEntity } {} @@ -303,18 +307,18 @@ namespace SHADE int i = 0; for (const auto& collider : GetNativeComponent()->GetCollisionShapes()) { - CollisionShape^ bound = nullptr; + CollisionShape^ shape = nullptr; switch (collider->GetType()) { - case SHCollisionShape::Type::BOX: - bound = gcnew BoxCollider(i, Owner.GetEntity()); - break; case SHCollisionShape::Type::SPHERE: - bound = gcnew SphereCollider(i, Owner.GetEntity()); + shape = gcnew SphereCollider(i, Owner.GetEntity()); + break; + case SHCollisionShape::Type::BOX: + shape = gcnew BoxCollider(i, Owner.GetEntity()); + break; + case SHCollisionShape::Type::CAPSULE: + shape = gcnew CapsuleCollider(i, Owner.GetEntity()); break; - //case SHCollisionShape::Type::CAPSULE: - // // TODO - // break; default: Debug::LogWarning("[Collider] An invalid Collider Type was detected. Skipping."); break; @@ -322,7 +326,7 @@ namespace SHADE ++i; // Add into list - subColliderList->Add(bound); + subColliderList->Add(shape); } } } \ No newline at end of file diff --git a/SHADE_Managed/src/Components/Collider.h++ b/SHADE_Managed/src/Components/Collider.h++ index b16e8063..8dc13e01 100644 --- a/SHADE_Managed/src/Components/Collider.h++ +++ b/SHADE_Managed/src/Components/Collider.h++ @@ -16,6 +16,7 @@ of DigiPen Institute of Technology is prohibited. // Primary Include #include "Component.hxx" + namespace SHADE { template @@ -28,7 +29,7 @@ namespace SHADE try { auto& shape = collider->GetCollisionShape(arrayIndex); - if (shape.GetType() != SHCollisionShape::Type::BOX) + if (shape.GetType() == SHCollisionShape::Type::INVALID) throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); return dynamic_cast(shape); diff --git a/SHADE_Managed/src/Components/Collider.hxx b/SHADE_Managed/src/Components/Collider.hxx index 3c7b060e..d934affd 100644 --- a/SHADE_Managed/src/Components/Collider.hxx +++ b/SHADE_Managed/src/Components/Collider.hxx @@ -87,23 +87,6 @@ namespace SHADE void set(float value); } - /*-----------------------------------------------------------------------------*/ - /* Usage Functions */ - /*-----------------------------------------------------------------------------*/ - /// - /// Checks if the specified point is within this shape's bounds. - /// - /// Point to test with. - /// True if the point is in the shape's bounds. - virtual bool TestPoint(Vector3 point) = 0; - /// - /// Computes a Raycast and checks if there is a collision with any object. - /// - /// The ray to cast. - /// Maximum distance for the raycast check. - /// True if the ray intersects with an object in the scene. - virtual bool Raycast(Ray ray, float maxDistance) = 0; - protected: /*-----------------------------------------------------------------------------*/ /* Constructors */ @@ -135,7 +118,39 @@ namespace SHADE }; /// - /// Box-shaped Collider Bound. + /// A Sphere Collider + /// + public ref class SphereCollider : public CollisionShape + { + public: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Center of the sphere. + /// + property Vector3 Center + { + Vector3 get(); + } + /// + /// Radius of the sphere/ + /// + property float Radius + { + float get(); + void set(float value); + } + + internal: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + SphereCollider(int arrayIndex, Entity attachedEntity); + }; + + /// + /// A Box Collider /// public ref class BoxCollider : public CollisionShape { @@ -166,14 +181,6 @@ namespace SHADE Quaternion get(); } - /*-----------------------------------------------------------------------------*/ - /* ColliderBound Functions */ - /*-----------------------------------------------------------------------------*/ - /// - bool TestPoint(Vector3 point) override; - /// - bool Raycast(Ray ray, float maxDistance) override; - internal: /*-----------------------------------------------------------------------------*/ /* Constructors */ @@ -182,44 +189,45 @@ namespace SHADE }; /// - /// Sphere-shaped Collider Bound. + /// A Capsule Collider /// - public ref class SphereCollider : public CollisionShape + public ref class CapsuleCollider : public CollisionShape { public: /*-----------------------------------------------------------------------------*/ /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// Center of the sphere. + /// Center of the capsule. /// property Vector3 Center { Vector3 get(); } /// - /// Radius of the Bounding Sphere formed by this bound. + /// Radius of the capsule. /// property float Radius { float get(); void set(float value); } - - /*-----------------------------------------------------------------------------*/ - /* ColliderBound Functions */ - /*-----------------------------------------------------------------------------*/ - /// - bool TestPoint(Vector3 point) override; - /// - bool Raycast(Ray ray, float maxDistance) override; + /// + /// Height of the capsule. + /// + property float Height + { + float get(); + void set(float value); + } internal: /*-----------------------------------------------------------------------------*/ /* Constructors */ /*-----------------------------------------------------------------------------*/ - SphereCollider(int arrayIndex, Entity attachedEntity); + CapsuleCollider(int arrayIndex, Entity attachedEntity); }; + /// /// CLR version of the the SHADE Engine's SHColliderComponent. diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx index e10111c3..db8bd412 100644 --- a/SHADE_Managed/src/Editor/Editor.cxx +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -18,6 +18,7 @@ of DigiPen Institute of Technology is prohibited. #include "Editor/Editor.hxx" // STL Includes #include +#include // Project Headers #include "Components/Component.hxx" #include "Scripts/ScriptStore.hxx" @@ -30,7 +31,9 @@ of DigiPen Institute of Technology is prohibited. #include "RangeAttribute.hxx" #include "Math/Vector2.hxx" #include "Math/Vector3.hxx" -#include +#include "Assets/AnimationClipAsset.hxx" +#include "Assets/AnimationControllerAsset.hxx" +#include "Assets/AnimationRigAsset.hxx" // Using Directives using namespace System; @@ -163,24 +166,27 @@ namespace SHADE bool isHovered = false; const bool MODIFIED_PRIMITIVE = - renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputCheckbox, &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputFloat , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputDouble , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputVec2 , &isHovered) || - renderSpecificField(field, object, SHEditorUI::InputVec3 , &isHovered) || - renderSpecificField(field, object, nullptr , &isHovered) || - renderSpecificField(field, object, nullptr , &isHovered) || - renderSpecificField(field, object, nullptr , &isHovered) || - renderSpecificField(field, object, nullptr , &isHovered) || - renderSpecificField(field, object, nullptr , &isHovered) || - renderSpecificField(field, object, nullptr , &isHovered); + renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputInt , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputCheckbox, &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputFloat , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputDouble , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputVec2 , &isHovered) || + renderSpecificField(field, object, SHEditorUI::InputVec3 , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered) || + renderSpecificField(field, object, nullptr , &isHovered); if (!MODIFIED_PRIMITIVE) { @@ -324,24 +330,27 @@ namespace SHADE bool modified; const bool RENDERED = - renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputCheckbox, nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputFloat , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputDouble , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputVec2 , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, SHEditorUI::InputVec3 , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || - renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified); + renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputCheckbox, nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputFloat , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputDouble , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputVec2 , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, SHEditorUI::InputVec3 , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified) || + renderFieldEditor(fieldName, object, nullptr , nullptr, rangeAttrib, modified); return modified; } diff --git a/SHADE_Managed/src/Editor/Editor.h++ b/SHADE_Managed/src/Editor/Editor.h++ index 37d5c27d..59d1f7d9 100644 --- a/SHADE_Managed/src/Editor/Editor.h++ +++ b/SHADE_Managed/src/Editor/Editor.h++ @@ -23,6 +23,9 @@ of DigiPen Institute of Technology is prohibited. #include "Assets/FontAsset.hxx" #include "Assets/MeshAsset.hxx" #include "Assets/MaterialAsset.hxx" +#include "Assets/AnimationClipAsset.hxx" +#include "Assets/AnimationControllerAsset.hxx" +#include "Assets/AnimationRigAsset.hxx" namespace SHADE { @@ -237,6 +240,42 @@ namespace SHADE return true; } + return false; + } + template<> + bool Editor::renderFieldEditorInternal(const std::string& fieldName, interior_ptr managedValPtr, EditorFieldFunc, 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(const std::string& fieldName, interior_ptr managedValPtr, EditorFieldFunc, 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(const std::string& fieldName, interior_ptr managedValPtr, EditorFieldFunc, bool* isHovered, RangeAttribute^) + { + uint32_t assetId = managedValPtr->NativeAssetID; + if (SHEditorUI::InputAssetField(fieldName, assetId, AssetType::MODEL, isHovered)) + { + *managedValPtr = AnimationRigAsset(assetId); + return true; + } + return false; } } diff --git a/SHADE_Managed/src/Engine/ECS.cxx b/SHADE_Managed/src/Engine/ECS.cxx index 5b73b64e..c79175bf 100644 --- a/SHADE_Managed/src/Engine/ECS.cxx +++ b/SHADE_Managed/src/Engine/ECS.cxx @@ -32,6 +32,8 @@ of DigiPen Institute of Technology is prohibited. #include "UI\SHUIComponent.h" #include "UI\SHSliderComponent.h" #include "UI\SHCanvasComponent.h" +#include "Animation\SHAnimatorComponent.h" +#include "Graphics\MiddleEnd\TrajectoryRendering\SHTrajectoryRenderableComponent.h" // Project Headers #include "Utility/Convert.hxx" #include "Utility/Debug.hxx" @@ -47,7 +49,7 @@ of DigiPen Institute of Technology is prohibited. #include "Components\Canvas.hxx" #include "Components\Slider.hxx" #include "Components\TrajectoryRenderable.hxx" -#include "Graphics\MiddleEnd\TrajectoryRendering\SHTrajectoryRenderableComponent.h" +#include "Components\Animator.hxx" @@ -338,6 +340,7 @@ namespace SHADE componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); + componentMap.Add(createComponentSet()); } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx b/SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx index b31209c1..aafd510e 100644 --- a/SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx @@ -23,6 +23,9 @@ of DigiPen Institute of Technology is prohibited. #include "Assets/MeshAsset.hxx" #include "Scripts/Script.hxx" #include "Scripts/ScriptStore.hxx" +#include "Assets/AnimationClipAsset.hxx" +#include "Assets/AnimationControllerAsset.hxx" +#include "Assets/AnimationRigAsset.hxx" /*-------------------------------------------------------------------------------------*/ /* File-Level Constants */ @@ -164,24 +167,27 @@ namespace SHADE YAML::Node fieldNode; // Retrieve string for the YAML - const bool PRIMITIVE_SERIALIZED = fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode); + const bool PRIMITIVE_SERIALIZED = fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml (fieldInfo, object, fieldNode); // Serialization of more complex types if (!PRIMITIVE_SERIALIZED) @@ -228,24 +234,27 @@ namespace SHADE bool SerialisationUtilities::varInsertYaml(System::Object^ object, YAML::Node& fieldNode) { const bool INSERTED = - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode) || - varInsertYamlInternal(object, fieldNode); + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode); return INSERTED; } @@ -255,24 +264,27 @@ namespace SHADE bool SerialisationUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) { const bool ASSIGNED = - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml(fieldInfo, object, node) || - fieldAssignYaml(fieldInfo, object, node) || - fieldAssignYaml(fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml(fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node); + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node); if (!ASSIGNED) { if (ReflectionUtilities::FieldIsList(fieldInfo)) @@ -329,24 +341,27 @@ namespace SHADE bool SerialisationUtilities::varAssignYaml(System::Object^% object, YAML::Node& node) { const bool DESERIALISED = - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal(object, node) || - varAssignYamlInternal(object, node) || - varAssignYamlInternal(object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal(object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node) || - varAssignYamlInternal (object, node); + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node) || + varAssignYamlInternal(object, node); return DESERIALISED; } } diff --git a/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ index d2043f6b..80f1e4aa 100644 --- a/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ +++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ @@ -60,9 +60,12 @@ namespace SHADE { fieldNode = MAX_EID; } - else if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v) + else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { fieldNode = INVALID_ASSET_ID; } @@ -128,9 +131,12 @@ namespace SHADE return true; } } - else if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v) + else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { if (object->GetType() == FieldType::typeid) { @@ -254,9 +260,12 @@ namespace SHADE const uint32_t EID = node.as(); object = (EID == MAX_EID ? GameObject() : GameObject(EID)); } - else if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v) + else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { if (object->GetType() == FieldType::typeid) {