Implemented Animation Clip asset and animation controller #410

Merged
XiaoQiDigipen merged 66 commits from SP3-22-AnimationController into main 2023-03-09 16:19:40 +08:00
5 changed files with 209 additions and 94 deletions
Showing only changes of commit 493f2c3cfe - Show all commits

View File

@ -22,20 +22,8 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/
SHAnimationController::AnimParam::AnimParam(Type type)
: ParamType { type }
{
switch (ParamType)
{
case Type::Bool:
Value = false;
break;
case Type::Float:
Value = 0.0f;
break;
case Type::Int:
Value = 0;
break;
}
}
, Value { 0.0f }
{}
/*-----------------------------------------------------------------------------------*/
/* Transition - Usage Functions */
@ -51,12 +39,12 @@ namespace SHADE
{
case AnimParam::Type::Bool:
case AnimParam::Type::Trigger:
return evaluateCondition(std::get<bool>(testParam.Value));
return evaluateCondition<bool>(testParam.Value != 0.0f);
case AnimParam::Type::Float:
return evaluateCondition(std::get<float>(testParam.Value));
return evaluateCondition<float>(testParam.Value);
break;
case AnimParam::Type::Int:
return evaluateCondition(std::get<int>(testParam.Value));
return evaluateCondition<int>(static_cast<int>(testParam.Value));
break;
}

View File

@ -59,7 +59,7 @@ namespace SHADE
Float,
Int
};
using ValueType = std::variant<bool, float, int>;
using ValueType = float;
/*-------------------------------------------------------------------------------*/
/* Constructor */
@ -104,7 +104,7 @@ namespace SHADE
/* Data Members */
/*-------------------------------------------------------------------------------*/
Handle<Node> Target;
ConditionType Condition;
ConditionType Condition = ConditionType::None;
AnimParam Param;
std::string ParamName;

View File

@ -17,12 +17,14 @@ namespace SHADE
template<typename T>
bool SHAnimationController::Transition::evaluateCondition(T value) const noexcept
{
// Early failure if invalid data
if (!std::holds_alternative<T>(Param.Value))
return false;
// Get the value
const T PARAM_VAL = std::get<T>(Param.Value);
const T PARAM_VAL = [&]()
{
if constexpr (std::is_floating_point_v<T>)
return Param.Value;
else
return Param.Value != 0.0f;
}();
// Handle condition type
switch (Condition)

View File

@ -97,12 +97,26 @@ namespace SHADE
// Draw
drawActiveMenuBar();
ImGui::BeginTable("base_table", 3, ImGuiTableFlags_Resizable);
ImGui::BeginTable("base_table", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable);
{
// Set up Columns
ImGui::TableSetupColumn("params_panel", ImGuiTableColumnFlags_WidthStretch, SIDE_PANELS_COLUMN_WIDTH);
ImGui::TableSetupColumn("main_panel", ImGuiTableColumnFlags_WidthFixed, MAIN_PANEL_COLUMN_WIDTH);
ImGui::TableSetupColumn("prop_panel", ImGuiTableColumnFlags_WidthStretch, SIDE_PANELS_COLUMN_WIDTH);
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();
@ -141,37 +155,48 @@ namespace SHADE
{
if (ImGui::BeginMenuBar())
{
// Save Button
if (ImGui::Button(std::format("{} Save", ICON_MD_SAVE).data()))
{
// TODO
}
if (ImGui::Button(std::format("{} Add", ICON_MD_ADD).data()))
// Discard Button
if (ImGui::Button(std::format("{} Discard Changes", ICON_MD_CANCEL).data()))
{
createNode(controllerData.value());
// TODO
}
ImGui::EndMenuBar();
}
}
void SHAnimationControllerEditor::drawParamsPanel()
void SHAnimationControllerEditor::drawParamsMenuBar()
{
// Add Parameter Button
if (ImGui::BeginCombo("##Type", "Add Parameter", ImGuiComboFlags_None))
if (ImGui::BeginCombo("##Type", std::format("{} Add Parameter", ICON_MD_ADD).data(), ImGuiComboFlags_None))
{
// All other options
for (int i = 0; i < static_cast<int>(typesList.size()); ++i)
{
if (ImGui::Selectable(typesList[i].c_str()))
{
controllerData->Params.emplace("New", static_cast<SHAnimationController::AnimParam::Type>(i));
int count = 0;
std::string paramName = "New Param";
while (controllerData->Params.contains(paramName))
{
paramName = "New Param " + std::to_string(++count);
}
controllerData->Params.emplace(paramName, static_cast<SHAnimationController::AnimParam::Type>(i));
}
}
ImGui::EndCombo();
}
}
void SHAnimationControllerEditor::drawParamsPanel()
{
int paramId = 0;
for (auto param : controllerData->Params)
for (const auto& param : controllerData->Params)
{
ImGui::PushID(paramId++);
if (SHEditorWidgets::InputText
@ -185,7 +210,15 @@ namespace SHADE
controllerData->Params.erase(param.first);
// Put into the new
controllerData->Params[val] = TYPE;
}
// TODO: This needs to be handled in a custom command
// Update all links
for (auto& link : controllerData->Links)
{
link.second.ParamName = val;
}
},
{}, ImGuiInputTextFlags_EnterReturnsTrue
))
{
ImGui::PopID();
@ -209,7 +242,30 @@ namespace SHADE
(
param.second,
static_cast<SHAnimationController::AnimParam::Type>(i),
[&](SHAnimationController::AnimParam::Type val) { controllerData->Params[param.first] = val; }
[&](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
@ -227,6 +283,33 @@ namespace SHADE
}
}
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() < 1);
if (ImGui::Button(std::format("{} Delete Nodes", ICON_MD_ADD).data()))
{
}
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()))
{
}
ImGui::EndDisabled();
}
void SHAnimationControllerEditor::drawNodeEditor()
{
static constexpr float NODE_WIDTH = 80.0f;
@ -330,6 +413,39 @@ namespace SHADE
}
}
void SHAnimationControllerEditor::drawPropertiesMenuBar()
{
// Set Starting Node Button
const int SELECTED_LINKS_COUNT = ImNodes::NumSelectedLinks();
ImGui::BeginDisabled(SELECTED_LINKS_COUNT < 1);
if (ImGui::Button(std::format("{} Reset Conditions", ICON_MD_SETTINGS_BACKUP_RESTORE).data()))
{
std::vector<int> selectedLinks(SELECTED_LINKS_COUNT);
ImNodes::GetSelectedLinks(selectedLinks.data());
for (auto& link : selectedLinks)
{
// Get LinkData
NodeLinkIndex nodeLinkIndex;
nodeLinkIndex.Raw = link;
if (!controllerData->Links.contains(nodeLinkIndex.Raw))
continue;
LinkData& linkData = controllerData->Links[nodeLinkIndex.Raw];
// Ensure that the link is valid
if (!controllerData->IndexToNodeMap.contains(nodeLinkIndex.SourceAttribute.OwnerNodeIndex) ||
!controllerData->IndexToNodeMap.contains(nodeLinkIndex.DestinationAttribute.OwnerNodeIndex))
{
continue;
}
linkData.ParamName = "";
linkData.Condition = SHAnimationController::Transition::ConditionType::None;
}
}
ImGui::EndDisabled();
}
void SHAnimationControllerEditor::drawPropertiesPanel()
{
const int SELECTED_LINKS_COUNT = ImNodes::NumSelectedLinks();
@ -432,71 +548,77 @@ namespace SHADE
{
const SHAnimationController::AnimParam::Type PARAM_TYPE = controllerData->Params[linkData.ParamName];
// Comparison Type
const auto& CURR_COMPARISON = conditionsList[static_cast<int>(linkData.Condition)];
ImGui::Text("Condition Type");
ImGui::SameLine();
if (ImGui::BeginCombo("##ConditionType", CURR_COMPARISON.c_str(), ImGuiComboFlags_None))
if (PARAM_TYPE != SHAnimationController::AnimParam::Type::Trigger)
{
// We only show equal and not equal for bool
const int LAST_ELEM = PARAM_TYPE == SHAnimationController::AnimParam::Type::Bool ? static_cast<int>(SHAnimationController::Transition::ConditionType::NotEquals)
: static_cast<int>(conditionsList.size() - 1);
// Comparisons
for (int i = 0; i <= LAST_ELEM; ++i)
// Comparison Type
const auto& CURR_COMPARISON = conditionsList[static_cast<int>(linkData.Condition)];
ImGui::Text("Condition Type");
ImGui::SameLine();
if (ImGui::BeginCombo("##ConditionType", CURR_COMPARISON.c_str(), ImGuiComboFlags_None))
{
const bool IS_SELECTED = i == static_cast<int>(linkData.Condition);
if (ImGui::Selectable(conditionsList[i].c_str(), IS_SELECTED))
// We only show equal and not equal for bool
const int LAST_ELEM = PARAM_TYPE == SHAnimationController::AnimParam::Type::Bool ? static_cast<int>(SHAnimationController::Transition::ConditionType::NotEquals)
: static_cast<int>(conditionsList.size() - 1);
// Comparisons
for (int i = 0; i <= LAST_ELEM; ++i)
{
SHCommandManager::PerformCommand
(
std::reinterpret_pointer_cast<SHBaseCommand>
const bool IS_SELECTED = i == static_cast<int>(linkData.Condition);
if (ImGui::Selectable(conditionsList[i].c_str(), IS_SELECTED))
{
SHCommandManager::PerformCommand
(
std::make_shared<SHCommand<SHAnimationController::Transition::ConditionType>>
std::reinterpret_pointer_cast<SHBaseCommand>
(
linkData.Condition,
static_cast<SHAnimationController::Transition::ConditionType>(i),
[&](SHAnimationController::Transition::ConditionType val) { linkData.Condition = val; }
)
),
false
);
}
std::make_shared<SHCommand<SHAnimationController::Transition::ConditionType>>
(
linkData.Condition,
static_cast<SHAnimationController::Transition::ConditionType>(i),
[&](SHAnimationController::Transition::ConditionType val) { linkData.Condition = val; }
)
),
false
);
}
if (IS_SELECTED)
if (IS_SELECTED)
{
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
// Parameter Value
if (linkData.Condition != SHAnimationController::Transition::ConditionType::None)
{
switch (PARAM_TYPE)
{
ImGui::SetItemDefaultFocus();
case SHAnimationController::AnimParam::Type::Bool:
SHEditorWidgets::CheckBox
(
"Required State",
[&]() { return linkData.ParamThresholdValue != 0.0f; },
[&](bool val) { linkData.ParamThresholdValue = static_cast<float>(val); }
);
break;
case SHAnimationController::AnimParam::Type::Float:
SHEditorWidgets::DragFloat
(
"Threshold",
[&]() { return linkData.ParamThresholdValue; },
[&](float val) { linkData.ParamThresholdValue = val; }
);
break;
case SHAnimationController::AnimParam::Type::Int:
SHEditorWidgets::DragInt
(
"Threshold",
[&]() { return static_cast<int>(linkData.ParamThresholdValue); },
[&](int val) { linkData.ParamThresholdValue = static_cast<float>(val); }
);
break;
}
}
ImGui::EndCombo();
}
// Parameter Value
switch (PARAM_TYPE)
{
case SHAnimationController::AnimParam::Type::Bool:
SHEditorWidgets::CheckBox
(
"Threshold",
[&](){ return std::get<bool>(linkData.ParamThresholdValue); },
[&](bool val) { linkData.ParamThresholdValue = val; }
);
break;
case SHAnimationController::AnimParam::Type::Float:
SHEditorWidgets::DragFloat
(
"Threshold",
[&]() { return std::get<float>(linkData.ParamThresholdValue); },
[&](float val) { linkData.ParamThresholdValue = val; }
);
break;
case SHAnimationController::AnimParam::Type::Int:
SHEditorWidgets::DragInt
(
"Threshold",
[&]() { return std::get<int>(linkData.ParamThresholdValue); },
[&](int val) { linkData.ParamThresholdValue = val; }
);
break;
}
}
}

View File

@ -91,7 +91,7 @@ namespace SHADE
NodeAttributeIndex SourceAttrib;
NodeAttributeIndex DestAttrib;
// Conditional Data
SHAnimationController::Transition::ConditionType Condition;
SHAnimationController::Transition::ConditionType Condition = SHAnimationController::Transition::ConditionType::None;
std::string ParamName;
SHAnimationController::AnimParam::ValueType ParamThresholdValue;
};
@ -120,8 +120,11 @@ namespace SHADE
/* 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);