diff --git a/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.cpp b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.cpp index 6285ffbd..b062e591 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.cpp @@ -22,6 +22,8 @@ of DigiPen Institute of Technology is prohibited. #include "Editor/IconsMaterialDesign.h" #include "Animation/SHAnimationController.h" #include "Editor/SHEditorUI.h" +#include "Editor/SHEditorWidgets.hpp" +#include "../../Command/SHCommand.hpp" namespace SHADE { @@ -39,6 +41,19 @@ namespace SHADE { SHEditorWindow::Init(); + // Set up caches + conditionsList = + { + "None", + "=", + "!=", + "<", + "<=", + ">", + ">=" + }; + + // Set up sample animation controller for testing SHAnimationController controller; auto n1 = controller.CreateNode(); auto n2 = controller.CreateNode(); @@ -90,7 +105,7 @@ namespace SHADE ImGui::TableSetColumnIndex(1); drawNodeEditor(); ImGui::TableSetColumnIndex(2); - ImGui::Text("Properties"); + drawPropertiesPanel(); } } ImGui::EndTable(); @@ -234,6 +249,188 @@ namespace SHADE } } } + + 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)) + { + linkData.ParamName = param.first; + 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]; + + // 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 + switch (PARAM_TYPE) + { + case SHAnimationController::AnimParam::Type::Bool: + SHEditorWidgets::CheckBox + ( + "Threshold", + [&](){ return std::get(linkData.ParamThresholdValue); }, + [&](bool val) { linkData.ParamThresholdValue = val; } + ); + break; + case SHAnimationController::AnimParam::Type::Float: + SHEditorWidgets::DragFloat + ( + "Threshold", + [&]() { return std::get(linkData.ParamThresholdValue); }, + [&](float val) { linkData.ParamThresholdValue = val; } + ); + break; + case SHAnimationController::AnimParam::Type::Int: + SHEditorWidgets::DragInt + ( + "Threshold", + [&]() { return std::get(linkData.ParamThresholdValue); }, + [&](int val) { linkData.ParamThresholdValue = val; } + ); + break; + } + } + } + + ImGui::PopID(); + } + } + else + { + ImGui::Text("Select an object to view properties."); + } + } + SHAnimationControllerEditor::NodeAttributeIndex SHAnimationControllerEditor::getExtraInputAttrib(uint32_t nodeIndex) { NodeAttributeIndex extraInputAttrib; @@ -266,17 +463,19 @@ namespace SHADE } std::list::iterator SHAnimationControllerEditor::createNode(AnimControllerData& data) { + const NodeIndex NEW_NODE_IDX = data.NextNodeIndex++; + Node localNode; - localNode.Index = data.NextNodeIndex++; + localNode.Index = NEW_NODE_IDX; data.Nodes.emplace_back(std::move(localNode)); // Update the node map auto nodeIter = --data.Nodes.end(); - data.IndexToNodeMap[localNode.Index] = nodeIter; + data.IndexToNodeMap[NEW_NODE_IDX] = nodeIter; return nodeIter; } - void SHAnimationControllerEditor::createLink(AnimControllerData& data, std::list::iterator sourceNode, std::list::iterator destNode) + SHAnimationControllerEditor::LinkMap::iterator SHAnimationControllerEditor::createLink(AnimControllerData& data, std::list::iterator sourceNode, std::list::iterator destNode) { // Update source node's output attributes NodeAttributeIndex attribIndex; @@ -299,8 +498,10 @@ namespace SHADE linkIdx.SourceAttribute = link.SourceAttrib; linkIdx.DestinationAttribute = link.DestAttrib; - data.Links.emplace(linkIdx.Raw, std::move(link)); + const auto EMPLACE_DATA = data.Links.emplace(linkIdx.Raw, std::move(link)); sourceNode->Transitions.emplace_back(linkIdx); + + return EMPLACE_DATA.first; } /*-----------------------------------------------------------------------------------*/ /* Static Helper Functions */ @@ -340,7 +541,10 @@ namespace SHADE auto targetNodeIter = nodeMap[transition.Target]; // Create link - createLink(data, dataNodeIter, targetNodeIter); + auto& linkData = createLink(data, dataNodeIter, targetNodeIter)->second; + linkData.Condition = transition.Condition; + linkData.ParamName = transition.ParamName; + linkData.ParamThresholdValue = transition.Param.Value; } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.h b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.h index d12e55cf..0b566270 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.h +++ b/SHADE_Engine/src/Editor/EditorWindow/Animation/SHAnimationControllerEditor.h @@ -52,6 +52,7 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ using NodeIndex = uint8_t; + using LinkIndex = int32_t; union NodeAttributeIndex { uint16_t Raw; @@ -63,7 +64,7 @@ namespace SHADE }; union NodeLinkIndex { - uint32_t Raw; + LinkIndex Raw; struct { NodeAttributeIndex SourceAttribute; @@ -84,17 +85,25 @@ namespace SHADE struct LinkData { - std::list::iterator SourceNode; - std::list::iterator TargetNode; - NodeAttributeIndex SourceAttrib; - NodeAttributeIndex DestAttrib; + // Source/Dest Data + std::list::iterator SourceNode; + std::list::iterator TargetNode; + NodeAttributeIndex SourceAttrib; + NodeAttributeIndex DestAttrib; + // Conditional Data + SHAnimationController::Transition::ConditionType Condition; + std::string ParamName; + SHAnimationController::AnimParam::ValueType ParamThresholdValue; }; + + using LinkMap = std::unordered_map; struct AnimControllerData { + std::list Nodes; std::unordered_map Params; - std::unordered_map Links; + LinkMap Links; int NextNodeIndex = 0; // Index to use for newly created nodes std::unordered_map::iterator> IndexToNodeMap; }; @@ -103,12 +112,15 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ std::optional controllerData; + // Persistent Cached Data + std::vector conditionsList; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ void drawActiveMenuBar(); void drawNodeEditor(); + void drawPropertiesPanel(); NodeAttributeIndex getExtraInputAttrib(uint32_t nodeIndex); NodeAttributeIndex getExtraOutputAttrib(uint32_t nodeIndex); void drawInputNode(int id, ImNodesPinShape_ pinShape); @@ -118,7 +130,7 @@ namespace SHADE /* Static Helper Functions */ /*---------------------------------------------------------------------------------*/ static std::list::iterator createNode(AnimControllerData& data); - static void createLink(AnimControllerData& data, std::list::iterator sourceNode, std::list::iterator destNode); + static LinkMap::iterator createLink(AnimControllerData& data, std::list::iterator sourceNode, std::list::iterator destNode); static AnimControllerData deserialise(const SHAnimationController& controller); static SHAnimationController serialise(const AnimControllerData& data); };