Compare commits

...

6 Commits

Author SHA1 Message Date
Kah Wei 26f84fb8d6 Split keyframes on animation editor into their constituents 2023-01-16 16:12:46 +08:00
Kah Wei 52913562e7 Adjusted SHAnimationEditor to fit new animation clip format 2023-01-16 15:26:51 +08:00
Kah Wei 3b5d1ef6d5 Merge branch 'SP3-17-animation-system' into SP3-17-animation-viewer
# Conflicts:
#	SHADE_Engine/src/Animation/SHAnimationClip.h
2023-01-16 14:48:57 +08:00
Kah Wei 1044d79243 Adjusted animation editor timeline frame to fit size of keyframe indicator 2023-01-09 16:59:50 +08:00
Kah Wei 1c56864ccb Centered timeline time indicators and keyframe icons 2023-01-09 13:37:40 +08:00
Kah Wei b93dfe3ba4 Added initial implementation of SHAnimationEditor 2023-01-08 23:39:59 +08:00
7 changed files with 335 additions and 8 deletions

View File

@ -60,6 +60,7 @@ namespace SHADE
/// Constructs an SHAnimation Clip from a specified SHAnimAsset. /// Constructs an SHAnimation Clip from a specified SHAnimAsset.
/// </summary> /// </summary>
/// <param name="asset">Animation asset to load.</param> /// <param name="asset">Animation asset to load.</param>
SHAnimationClip() = default;
explicit SHAnimationClip(const SHAnimAsset& asset); explicit SHAnimationClip(const SHAnimAsset& asset);
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -68,14 +69,16 @@ namespace SHADE
const std::vector<Channel>& GetChannels() const noexcept { return channels; } const std::vector<Channel>& GetChannels() const noexcept { return channels; }
int GetTicksPerSecond() const noexcept { return ticksPerSecond; } int GetTicksPerSecond() const noexcept { return ticksPerSecond; }
float GetTotalTime() const noexcept { return totalTime; } float GetTotalTime() const noexcept { return totalTime; }
int GetTotalFrames() const noexcept { return totalFrames; }
private: //private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
std::vector<Channel> channels; std::vector<Channel> channels;
int ticksPerSecond; int ticksPerSecond;
float totalTime; float totalTime;
int totalFrames;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */

View File

@ -0,0 +1,194 @@
/************************************************************************************//*!
\file SHAnimationClip.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 SHAnimationClip 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 "SHAnimationEditor.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors */
/*-----------------------------------------------------------------------------------*/
SHAnimationEditor::SHAnimationEditor()
: SHEditorWindow(ICON_MD_ANIMATION" Animation Editor", ImGuiWindowFlags_MenuBar)
{
// Construct a simple test clip
clip.channels.emplace_back
(
SHAnimationClip::Channel
{
.Name = "Child 1",
.PositionKeyFrames =
{
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 0,
.Data = SHVec3 { 1.0f, 0.0f, 0.0f }
},
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 30,
.Data = SHVec3 { 0.0f, 0.0f, 0.0f }
},
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 60,
.Data = SHVec3 { 1.0f, 0.0f, 0.0f }
}
}
}
);
clip.channels.emplace_back
(
SHAnimationClip::Channel
{
.Name = "Child 2",
.PositionKeyFrames =
{
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 0,
.Data = SHVec3 { 1.0f, 0.0f, 0.0f }
},
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 30,
.Data = SHVec3 { 0.0f, 0.0f, 0.0f }
}
}
}
);
clip.channels.emplace_back
(
SHAnimationClip::Channel
{
.Name = "Child 3",
.PositionKeyFrames =
{
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 15,
.Data = SHVec3 { 1.0f, 0.0f, 0.0f }
},
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 30,
.Data = SHVec3 { 0.0f, 0.0f, 0.0f }
},
SHAnimationKeyFrame<SHVec3>
{
.FrameIndex = 45,
.Data = SHVec3 { 1.0f, 0.0f, 0.0f }
}
}
}
);
clip.totalFrames = 60;
clip.totalTime = static_cast<float>(clip.totalFrames) / clip.GetTicksPerSecond();
}
/*-----------------------------------------------------------------------------------*/
/* Editor Lifecycle */
/*-----------------------------------------------------------------------------------*/
void SHAnimationEditor::Init()
{
SHEditorWindow::Init();
}
void SHAnimationEditor::Update()
{
SHEditorWindow::Update();
// Constants
static constexpr float CHANNELS_COL_WIDTH = 150.0f;
static constexpr float TMP_TABLE_PADDING = 50.0f;
// Render the window
if (Begin())
{
// Construct 2-column table of the channels on the left
if (ImGui::BeginTable("animWindowTable", 2, ImGuiTableFlags_None))
{
// Set up Columns
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_WidthFixed, CHANNELS_COL_WIDTH);
ImGui::TableSetupColumn("");
// Compute dimensions for the clip
const float COL_WIDTH = windowSize.x - CHANNELS_COL_WIDTH - TMP_TABLE_PADDING;
const float DIST_PER_FRAME = 18.0f;//COL_WIDTH / static_cast<float>(clip.GetTotalFrames()); // Hardcoded distance based on the keyframe icon size
// Get Resources
const auto WINDOW_POS = ImGui::GetWindowPos();
auto drawList = ImGui::GetForegroundDrawList();
// Render Header
ImGui::TableHeadersRow();
ImGui::SameLine();
const auto TIMELINE_HEADER_START_POS = ImGui::GetCursorPos();
static constexpr int STEP = 10;
for (int frame = 0; frame <= clip.GetTotalFrames(); frame += STEP)
{
const float X_OFFSET = TIMELINE_HEADER_START_POS.x + frame * DIST_PER_FRAME;
// Draw Text
ImGui::SetCursorPos(ImVec2(X_OFFSET, TIMELINE_HEADER_START_POS.y));
SHEditorUI::Text(std::to_string(frame));
// Draw line
ImVec2 startPoint { WINDOW_POS.x + X_OFFSET, WINDOW_POS.y + TIMELINE_HEADER_START_POS.y };
drawList->AddLine(startPoint, ImVec2(startPoint.x, startPoint.y + beginContentRegionAvailable.y), ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f)));
// Draw smaller lines
for (int innerFrames = frame + 1; innerFrames < std::min(frame + STEP, clip.GetTotalFrames()); ++innerFrames)
{
ImVec2 innerStartPoint{ WINDOW_POS.x + TIMELINE_HEADER_START_POS.x + innerFrames * DIST_PER_FRAME, WINDOW_POS.y + TIMELINE_HEADER_START_POS.y };
drawList->AddLine(innerStartPoint, ImVec2(innerStartPoint.x, innerStartPoint.y + beginContentRegionAvailable.y), ImGui::GetColorU32(ImVec4(0.2f, 0.2f, 0.2f, 1.0f)));
}
}
ImGui::SetCursorPos(TIMELINE_HEADER_START_POS);
// Render all channels
for (const auto& channel : clip.channels)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
const bool SHOW_ALL = ImGui::CollapsingHeader(channel.Name.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
ImGui::TableNextColumn();
if (SHOW_ALL)
{
drawChannelKeyFrames("Position", channel.PositionKeyFrames);
drawChannelKeyFrames("Rotation", channel.RotationKeyFrames);
drawChannelKeyFrames("Scale", channel.ScaleKeyFrames);
}
else
{
// TODO: Show flattened view
}
}
ImGui::EndTable();
}
}
ImGui::End();
}
/*-----------------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
}

View File

@ -0,0 +1,57 @@
/************************************************************************************//*!
\file SHAnimationEditor.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Jan 8, 2023
\brief Contains the definition of the SHAnimationEditor class 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
// Project Includes
#include "Editor/EditorWindow/SHEditorWindow.h"
#include "Animation/SHAnimationClip.h"
namespace SHADE
{
/// <summary>
/// ImGui window that contains the SHAnimationEditor
/// </summary>
class SHAnimationEditor final : public SHEditorWindow
{
public:
/*---------------------------------------------------------------------------------*/
/* Constructor/Destructors */
/*---------------------------------------------------------------------------------*/
SHAnimationEditor();
virtual ~SHAnimationEditor() = default;
/*---------------------------------------------------------------------------------*/
/* Editor Lifecycle */
/*---------------------------------------------------------------------------------*/
void Init() override;
void Update() override;
private:
/*---------------------------------------------------------------------------------*/
/* Static Constants */
/*---------------------------------------------------------------------------------*/
static constexpr float DIST_PER_FRAME = 18.0f;
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
SHAnimationClip clip;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
template<typename T>
void drawChannelKeyFrames(const std::string& name, const std::vector<SHAnimationKeyFrame<T>>& keyframes);
};
}
#include "SHAnimationEditor.hpp"

View File

@ -0,0 +1,47 @@
/************************************************************************************//*!
\file SHAnimationEditor.hpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Jan 16, 2023
\brief Contains the definition of the SHAnimationEditor function templates.
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
// Primary Include
#include "SHAnimationEditor.h"
// Project Include
#include "Editor/SHEditorUI.h"
// External Dependencies
#include <imgui.h>
#include "Editor/IconsMaterialDesign.h"
namespace SHADE
{
template<typename T>
void SHAnimationEditor::drawChannelKeyFrames(const std::string& name, const std::vector<SHAnimationKeyFrame<T>>& keyframes)
{
// Ignore if keyframes list is empty
if (keyframes.empty())
return;
// Title
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Indent();
ImGui::Text(name.c_str());
ImGui::Unindent();
ImGui::TableNextColumn();
// Draw all the keyframes
const auto START_POS = ImGui::GetCursorPos();
for (const auto& keyframe : keyframes)
{
ImGui::SetCursorPos(ImVec2(START_POS.x + keyframe.FrameIndex * DIST_PER_FRAME, START_POS.y));
SHEditorUI::OffsetText(ICON_MD_RADIO_BUTTON_CHECKED, 6.0f); // Hardcoded offset to align icon since ImGui can't calculate icon with properly
}
ImGui::SetCursorPos(START_POS);
}
}

View File

@ -31,6 +31,7 @@
//#==============================================================# //#==============================================================#
#include "EditorWindow/SHEditorWindowManager.h" #include "EditorWindow/SHEditorWindowManager.h"
#include "EditorWindow/SHEditorWindowIncludes.h" #include "EditorWindow/SHEditorWindowIncludes.h"
#include "EditorWindow/AnimationEditor/SHAnimationEditor.h"
//#==============================================================# //#==============================================================#
//|| Library Includes || //|| Library Includes ||
@ -104,6 +105,7 @@ namespace SHADE
SHEditorWindowManager::CreateEditorWindow<SHAssetBrowser>(); SHEditorWindowManager::CreateEditorWindow<SHAssetBrowser>();
SHEditorWindowManager::CreateEditorWindow<SHMaterialInspector>(); SHEditorWindowManager::CreateEditorWindow<SHMaterialInspector>();
SHEditorWindowManager::CreateEditorWindow<SHColliderTagPanel>(); SHEditorWindowManager::CreateEditorWindow<SHColliderTagPanel>();
SHEditorWindowManager::CreateEditorWindow<SHAnimationEditor>();
SHEditorWindowManager::CreateEditorWindow<SHEditorViewport>(); SHEditorWindowManager::CreateEditorWindow<SHEditorViewport>();

View File

@ -137,6 +137,18 @@ namespace SHADE
{ {
ImGui::Text(title.c_str()); ImGui::Text(title.c_str());
} }
void SHEditorUI::CenteredText(const std::string& title)
{
OffsetText(title, -ImGui::CalcTextSize(title.c_str()).x * 1.5f);
}
void SHEditorUI::OffsetText(const std::string& title, float offset)
{
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offset);
ImGui::Text(title.c_str());
}
bool SHEditorUI::SmallButton(const std::string& title) bool SHEditorUI::SmallButton(const std::string& title)
{ {
return ImGui::SmallButton(title.c_str()); return ImGui::SmallButton(title.c_str());

View File

@ -90,7 +90,7 @@ namespace SHADE
/// <returns>True if the header is open, false otherwise.</returns> /// <returns>True if the header is open, false otherwise.</returns>
static bool CollapsingHeader(const std::string& title, bool* isHovered = nullptr); static bool CollapsingHeader(const std::string& title, bool* isHovered = nullptr);
static void SameLine(); static void SameLine();
static void Separator(); static void Separator();
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* ImGui Wrapper Functions - Queries */ /* ImGui Wrapper Functions - Queries */
@ -98,9 +98,9 @@ namespace SHADE
static bool IsItemHovered(); static bool IsItemHovered();
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* ImGui Wrapper Functions - Menu */ /* ImGui Wrapper Functions - Menu */
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
static bool BeginMenu(const std::string& label); static bool BeginMenu(const std::string& label);
static bool BeginMenu(const std::string& label, const char* icon); static bool BeginMenu(const std::string& label, const char* icon);
static void EndMenu(); static void EndMenu();
static void BeginTooltip(); static void BeginTooltip();
@ -150,6 +150,19 @@ namespace SHADE
/// <param name="title">Text to display.</param> /// <param name="title">Text to display.</param>
static void Text(const std::string& title); static void Text(const std::string& title);
/// <summary> /// <summary>
/// Creates a visual text widget that is visually centered about the current
/// cursor point.
/// </summary>
/// <param name="title">Text to display.</param>
static void CenteredText(const std::string& title);
/// <summary>
/// Creates a visual text widget that is visually offset from the current cursor
/// point.
/// </summary>
/// <param name="title">Text to display.</param>
/// <param name="offset">Distance to offset. Positive is to the right.</param>
static void OffsetText(const std::string& title, float offset);
/// <summary>
/// Creates a small inline button widget. /// Creates a small inline button widget.
/// <br/> /// <br/>
/// Wraps up ImGui::SmallButton(). /// Wraps up ImGui::SmallButton().
@ -164,8 +177,8 @@ namespace SHADE
/// </summary> /// </summary>
/// <param name="title">Text to display.</param> /// <param name="title">Text to display.</param>
/// <returns>True if button was pressed.</returns> /// <returns>True if button was pressed.</returns>
static bool Button(const std::string& title); static bool Button(const std::string& title);
static bool Selectable(const std::string& label); static bool Selectable(const std::string& label);
static bool Selectable(const std::string& label, const char* icon); static bool Selectable(const std::string& label, const char* icon);
/// <summary> /// <summary>
/// Creates a checkbox widget for boolean input. /// Creates a checkbox widget for boolean input.
@ -341,7 +354,6 @@ namespace SHADE
/// <returns>Whether the value was modified.</returns> /// <returns>Whether the value was modified.</returns>
static bool InputEnumCombo(const std::string& label, int& v, const std::vector<std::string>& enumNames, bool* isHovered = nullptr); static bool InputEnumCombo(const std::string& label, int& v, const std::vector<std::string>& enumNames, bool* isHovered = nullptr);
private: private:
// Prevent instantiation of this static class // Prevent instantiation of this static class
SHEditorUI() = delete; SHEditorUI() = delete;