From 7e8da1defa14aadd6846103b640af74b97384f4a Mon Sep 17 00:00:00 2001 From: Pentalimbed Date: Fri, 13 May 2022 21:19:11 +0800 Subject: [PATCH] 0.6a - 0.6a Editing character file (anim name & properties) - 0.6a CRC32 Macro - 0.6a char prop in behavior file can edit type now --- TODO | 9 +- cmake/headerlist.cmake | 1 - cmake/sourcelist.cmake | 1 - src/hkx/hkclass.inl | 15 +++ src/hkx/linkedmanager.h | 3 +- src/ui/charedit.cpp | 121 ----------------------- src/ui/charedit.h | 25 ----- src/ui/macros.cpp | 44 ++++++++- src/ui/macros.h | 23 ++++- src/ui/mainwindow.cpp | 14 +++ src/ui/propedit.cpp | 4 +- src/ui/varedit.cpp | 212 ++++++++++++++++++++++++++++++++++++---- src/ui/varedit.h | 9 +- src/ui/widgets.cpp | 23 +++-- src/ui/widgets.h | 78 +++++++-------- src/utils.h | 74 ++++++++++++++ 16 files changed, 424 insertions(+), 232 deletions(-) delete mode 100644 src/ui/charedit.cpp delete mode 100644 src/ui/charedit.h diff --git a/TODO b/TODO index 8fc01b4..de0c7fa 100644 --- a/TODO +++ b/TODO @@ -32,7 +32,12 @@ - 0.5.1a Blending transition hotfix - 0.5.1a Transition array hotfix +- 0.6a Editing character file (anim name & properties) +- 0.6a CRC32 Macro +- 0.6a char prop in behavior file can edit type now + +Delete all children Behaviour diagnosis -Display names within the var/state/ref/evt edit when not editing (using combobox?) -Editing character file (anim name & properties) +Scrolling names for var/state/ref/evt edit Nested state id picker +Maybe cleanup replicate varedit code? \ No newline at end of file diff --git a/cmake/headerlist.cmake b/cmake/headerlist.cmake index 15a61b1..6fede9b 100644 --- a/cmake/headerlist.cmake +++ b/cmake/headerlist.cmake @@ -19,5 +19,4 @@ set(headers src/ui/propedit.h src/ui/columnview.h src/ui/macros.h - src/ui/charedit.h ) diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index 1717f77..512040d 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -16,5 +16,4 @@ set(sources src/ui/propedit.cpp src/ui/columnview.cpp src/ui/macros.cpp - src/ui/charedit.cpp ) \ No newline at end of file diff --git a/src/hkx/hkclass.inl b/src/hkx/hkclass.inl index 28330d7..b2f400e 100644 --- a/src/hkx/hkclass.inl +++ b/src/hkx/hkclass.inl @@ -48,8 +48,23 @@ inline VariableTypeEnum getVarTypeEnum(std::string_view enumstr) return VARIABLE_TYPE_INVALID; } + #define DEFENUM(name) constexpr auto name = std::to_array +DEFENUM(e_variableTypeEnum) // for prop edit +({ + {"VARIABLE_TYPE_INVALID"}, + {"VARIABLE_TYPE_BOOL"}, + {"VARIABLE_TYPE_INT8"}, + {"VARIABLE_TYPE_INT16"}, + {"VARIABLE_TYPE_INT32"}, + {"VARIABLE_TYPE_REAL"}, + {"VARIABLE_TYPE_POINTER"}, + {"VARIABLE_TYPE_VECTOR3"}, + {"VARIABLE_TYPE_VECTOR4"}, + {"VARIABLE_TYPE_QUATERNION"}, +}); + // hkbVariableInfo -> hkbRoleAttribute DEFENUM(e_hkbRoleAttribute_Role) ({ diff --git a/src/hkx/linkedmanager.h b/src/hkx/linkedmanager.h index 347aae7..0687365 100644 --- a/src/hkx/linkedmanager.h +++ b/src/hkx/linkedmanager.h @@ -192,8 +192,7 @@ class CharacterPropertyManager : public LinkedPropertyManager { auto retval = LinkedPropertyManager::addEntry(); - retval.get().getByName("type").text() = "VARIABLE_TYPE_POINTER"; - retval.get().getByName("role").first_child().getByName("flags").text() = "FLAG_OUTPUT|FLAG_HIDDEN|FLAG_NOT_VARIABLE|FLAG_NONE"; + retval.get().getByName("role").first_child().getByName("flags").text() = "FLAG_NOT_VARIABLE|FLAG_NONE"; return retval; } diff --git a/src/ui/charedit.cpp b/src/ui/charedit.cpp deleted file mode 100644 index ad4fcc8..0000000 --- a/src/ui/charedit.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "charedit.h" -#include "widgets.h" -#include "hkx/hkclass.inl" - -#include - -namespace Haviour -{ -namespace Ui -{ -CharEdit* CharEdit::getSingleton() -{ - static CharEdit edit; - return std::addressof(edit); -} - -void CharEdit::show() -{ -} - -void CharEdit::showPropList() -{ - ImGui::PushID("propedit"); - constexpr auto table_flag = - ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody; - - bool scroll_to_bottom = false; - auto file_manager = Hkx::HkxFileManager::getSingleton(); - auto& current_file = file_manager->m_char_file; - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Character Property"), ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLUS_CIRCLE)) - ImGui::OpenPopup("Type Select"); - addTooltip("Add new character property"); - if (ImGui::BeginPopup("Type Select")) - { - for (auto data_type : Hkx::e_variableType) - if ((data_type != "VARIABLE_TYPE_INVALID") && ImGui::Selectable(data_type.data())) - { - ImGui::CloseCurrentPopup(); - scroll_to_bottom = true; - current_file.m_prop_manager.addEntry(Hkx::getVarTypeEnum(data_type)); - break; - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_HASHTAG)) - current_file.m_prop_manager.reindex(); - addTooltip("Reindex variables\nDiscard all variables marked obsolete"); - - ImGui::InputText("Filter", &m_prop_filter), ImGui::SameLine(); - ImGui::Button(ICON_FA_QUESTION_CIRCLE); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::TextColored(g_color_invalid, "Invalid"); - ImGui::TextColored(g_color_bool, "Boolean"); - ImGui::TextColored(g_color_int, "Int8/16/32"); - ImGui::TextColored(g_color_float, "Real"); - ImGui::TextColored(g_color_attr, "Pointer"); - ImGui::TextColored(g_color_quad, "Vector/Quaternion"); - ImGui::EndTooltip(); - } - ImGui::Separator(); - - if (ImGui::BeginTable("##PropList", 2, table_flag, ImVec2(-FLT_MIN, -FLT_MIN))) - { - ImGui::TableSetupColumn("id", ImGuiTableColumnFlags_WidthFixed, 36); - ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableNextRow(); - - auto var_list = current_file.m_prop_manager.getEntryList(); - std::erase_if(var_list, - [=](auto& var) { - auto disp_name = std::format("{:3} {}", var.m_index, var.get().text().as_string()); // :3 - return !(var.m_valid && - (m_prop_filter.empty() || - !std::ranges::search(disp_name, m_prop_filter, [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }).empty())); - }); - - ImGuiListClipper clipper; - clipper.Begin(var_list.size()); - while (clipper.Step()) - for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) - { - auto& var = var_list[row_n]; - - ImGui::TableNextColumn(); - - auto var_type = var.get().getByName("type").text().as_string(); - ImGui::PushStyleColor(ImGuiCol_Text, getVarColor(var)); - ImGui::Text("%d", var.m_index); - addTooltip(var_type); - ImGui::PopStyleColor(); - - ImGui::TableNextColumn(); - const bool is_selected = false; - if (ImGui::Selectable(std::format("{}##{}", var.get().text().as_string(), var.m_index).c_str(), is_selected)) - { - m_prop_current = var; - ImGui::OpenPopup("Editing Varibale"); - } - addTooltip("Click to edit"); - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - // varEditPopup("Editing Varibale", m_prop_current, current_file); - - if (scroll_to_bottom) - ImGui::SetScrollHereY(1.0f); - - ImGui::EndTable(); - } - ImGui::PopID(); -} - -} // namespace Ui -} // namespace Haviour \ No newline at end of file diff --git a/src/ui/charedit.h b/src/ui/charedit.h deleted file mode 100644 index 5099243..0000000 --- a/src/ui/charedit.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "hkx/linkedmanager.h" - -namespace Haviour -{ -namespace Ui -{ -class CharEdit -{ -public: - static CharEdit* getSingleton(); - - void show(); - - bool m_show = false; - -private: - std::string m_prop_filter = {}; - Hkx::Variable m_prop_current = {}; - - void showPropList(); -}; -} // namespace Ui -} // namespace Haviour \ No newline at end of file diff --git a/src/ui/macros.cpp b/src/ui/macros.cpp index 937f8d7..d2a7cb2 100644 --- a/src/ui/macros.cpp +++ b/src/ui/macros.cpp @@ -4,6 +4,8 @@ #include "hkx/hkutils.h" #include "hkx/hkclass.inl" +#include + #include #include @@ -19,7 +21,7 @@ void MacroModal::open(pugi::xml_node working_obj, Hkx::HkxFile* file) } void MacroModal::show() { - if (!m_working_obj || !m_file) + if (getClass() && (!m_working_obj || !m_file)) { m_open = false; return; @@ -126,6 +128,44 @@ void TriggerMacro::parse() } } +//////////////////// CRC32 + +void Crc32Macro::open(pugi::xml_node working_obj, Hkx::HkxFile* file) +{ + MacroModal::open(working_obj, file); + m_path = {}; +} + +void Crc32Macro::drawUi() +{ + ImGui::TextUnformatted(getHint()); + ImGui::Separator(); + if (ImGui::InputTextWithHint("Path", R"(meshes\actors\character\animations\1hm_attackright.hkx)", &m_path, ImGuiInputTextFlags_EnterReturnsTrue)) + { + auto temp_path = m_path; + + for (auto& ch : temp_path) + if (ch == '/') ch = '\\'; + transform(temp_path.begin(), temp_path.end(), temp_path.begin(), ::tolower); + + std::filesystem::path path(temp_path); + if (path.has_stem() && path.has_parent_path()) + { + auto parent = path.parent_path().string(); + auto stem = path.stem().string(); + + m_out_crc = fmt::format("{}\n{}\n{}", + Crc32::update(parent.data(), parent.length()), + Crc32::update(stem.data(), stem.length()), + 7891816); + } + } + addTooltip("Press Enter to convert"); + ImGui::InputTextMultiline("Output", &m_out_crc, {}, ImGuiInputTextFlags_ReadOnly); +} + +//////////////////// Macro manager + MacroManager* MacroManager::getSingleton() { static MacroManager manager; @@ -135,6 +175,8 @@ MacroManager* MacroManager::getSingleton() MacroManager::MacroManager() { m_macros.emplace_back(std::make_unique()); + m_macros.emplace_back(std::make_unique()); + m_file_listener = Hkx::HkxFileManager::getSingleton()->appendListener(Hkx::kEventFileChanged, [=]() { for (auto& macro : m_macros) { diff --git a/src/ui/macros.h b/src/ui/macros.h index 1d74c69..23f0b7e 100644 --- a/src/ui/macros.h +++ b/src/ui/macros.h @@ -19,7 +19,7 @@ class MacroModal friend class MacroManager; virtual constexpr const char* getName() = 0; - virtual constexpr const char* getClass() = 0; + virtual constexpr const char* getClass() = 0; // nullptr = no objects / files, empty string = any objects virtual constexpr const char* getHint() = 0; virtual void open(pugi::xml_node working_obj, Hkx::HkxFile* file); virtual void show(); @@ -57,6 +57,27 @@ class TriggerMacro : public MacroModal void parse(); }; +class Crc32Macro : public MacroModal +{ +public: + virtual void open(pugi::xml_node working_obj, Hkx::HkxFile* file) override; + virtual constexpr const char* getName() override { return "CRC32"; } + virtual constexpr const char* getClass() override { return nullptr; } + virtual constexpr const char* getHint() override + { + return "This macro will generate crc32 checksum for an animation path.\n" + "All capital letters with be converted to lower case, and all / to \\\n" + "Extension doesn't matter because this is only for hkx files."; + } + virtual void drawUi() override; + +private: + std::string m_path; + std::string m_out_crc; + + void parse(); +}; + /////////////////////////// MACRO MANAGER class MacroManager diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 2f5b196..3553d3a 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -282,6 +282,20 @@ void showMenuBar() file_manager->getCurrentFile()->buildRefList(); if (ImGui::MenuItem("Reindex Objects", nullptr, false, file_manager->isCurrentFileReady())) file_manager->getCurrentFile()->reindexObj(); + + ImGui::Separator(); + + for (auto& macro : MacroManager::getSingleton()->getMacros()) + if (!macro->getClass()) + { + if (ImGui::MenuItem(macro->getName())) + { + ImGui::CloseCurrentPopup(); + macro->open({}, nullptr); + } + addTooltip(macro->getHint()); + } + ImGui::EndMenu(); } if (ImGui::BeginMenu("Window")) diff --git a/src/ui/propedit.cpp b/src/ui/propedit.cpp index 22e7a2f..999cbfc 100644 --- a/src/ui/propedit.cpp +++ b/src/ui/propedit.cpp @@ -159,15 +159,13 @@ void PropEdit::show() } ImGui::SameLine(); if (ImGui::Button(ICON_FA_SCROLL)) - { ImGui::OpenPopup("Macro Select"); - } addTooltip("Macros"); if (ImGui::BeginPopup("Macro Select")) { bool has_something = false; for (auto& macro : MacroManager::getSingleton()->getMacros()) - if (!strcmp(macro->getClass(), class_str)) + if (macro->getClass() && !strcmp(macro->getClass(), class_str)) { has_something = true; if (ImGui::Selectable(macro->getName())) diff --git a/src/ui/varedit.cpp b/src/ui/varedit.cpp index d33ef12..d18132d 100644 --- a/src/ui/varedit.cpp +++ b/src/ui/varedit.cpp @@ -48,7 +48,18 @@ void VarEdit::show() showEvtList(); ImGui::EndTable(); } - } // Charcter file here. + } + else if (file->getType() == Hkx::HkxFile::kCharacter) + { + if (ImGui::BeginTable("varlisttbl", 2)) + { + ImGui::TableNextColumn(); + showCharPropList(); + ImGui::TableNextColumn(); + showAnimList(); + ImGui::EndTable(); + } + } else ImGui::TextDisabled("Incompatible hkx type."); } @@ -66,8 +77,7 @@ void VarEdit::showVarList() ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody; bool scroll_to_bottom = false; - auto file_manager = Hkx::HkxFileManager::getSingleton(); - auto& current_file = *dynamic_cast(file_manager->getCurrentFile()); + auto& file = *dynamic_cast(Hkx::HkxFileManager::getSingleton()->getCurrentFile()); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Variables"), ImGui::SameLine(); @@ -81,18 +91,18 @@ void VarEdit::showVarList() { ImGui::CloseCurrentPopup(); scroll_to_bottom = true; - current_file.m_var_manager.addEntry(Hkx::getVarTypeEnum(data_type)); + file.m_var_manager.addEntry(Hkx::getVarTypeEnum(data_type)); break; } ImGui::EndPopup(); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_FILTER)) - current_file.cleanupVariables(); + file.cleanupVariables(); addTooltip("Remove unused variables (USE WITH CAUTION!)"); ImGui::SameLine(); if (ImGui::Button(ICON_FA_HASHTAG)) - current_file.reindexVariables(); + file.reindexVariables(); addTooltip("Reindex variables\nDiscard all variables marked obsolete"); ImGui::InputText("Filter", &m_var_filter), ImGui::SameLine(); @@ -116,13 +126,11 @@ void VarEdit::showVarList() ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); - auto var_list = current_file.m_var_manager.getEntryList(); + auto var_list = file.m_var_manager.getEntryList(); std::erase_if(var_list, [=](auto& var) { auto var_disp_name = std::format("{:3} {}", var.m_index, var.get().text().as_string()); // :3 - return !(var.m_valid && - (m_var_filter.empty() || - !std::ranges::search(var_disp_name, m_var_filter, [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }).empty())); + return !(var.m_valid && hasText(var_disp_name, m_var_filter)); }); ImGuiListClipper clipper; @@ -163,7 +171,8 @@ void VarEdit::showVarList() if (is_selected) ImGui::SetItemDefaultFocus(); } - varEditPopup("Editing Varibale", m_var_current, current_file); + varEditPopup( + "Editing Varibale", m_var_current, file.m_var_manager, [&](size_t idx) { return file.getFirstVarRef(idx); }, file); if (scroll_to_bottom) ImGui::SetScrollHereY(1.0f); @@ -216,9 +225,7 @@ void VarEdit::showEvtList() std::erase_if(evt_list, [=](auto& evt) { auto disp_name = std::format("{:3} {}", evt.m_index, evt.get().text().as_string()); // :3 - return !(evt.m_valid && - (m_evt_filter.empty() || - !std::ranges::search(disp_name, m_evt_filter, [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }).empty())); + return !(evt.m_valid && hasText(disp_name, m_evt_filter)); }); ImGuiListClipper clipper; @@ -296,9 +303,7 @@ void VarEdit::showPropList() prop_list, [=](auto& prop) { auto disp_name = std::format("{:3} {}", prop.m_index, prop.get().text().as_string()); // :3 - return !(prop.m_valid && - (m_prop_filter.empty() || - !std::ranges::search(disp_name, m_prop_filter, [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }).empty())); + return !(prop.m_valid && hasText(disp_name, m_prop_filter)); }); ImGuiListClipper clipper; @@ -331,5 +336,178 @@ void VarEdit::showPropList() } ImGui::PopID(); } + +void VarEdit::showCharPropList() +{ + ImGui::PushID("charpropedit"); + constexpr auto table_flag = + ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | + ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody; + + bool scroll_to_bottom = false; + auto& file = *dynamic_cast(Hkx::HkxFileManager::getSingleton()->getCurrentFile()); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Character Properties"), ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLUS_CIRCLE)) + ImGui::OpenPopup("Type Select"); + addTooltip("Add new property"); + if (ImGui::BeginPopup("Type Select")) + { + for (auto data_type : Hkx::e_variableType) + if ((data_type != "VARIABLE_TYPE_INVALID") && ImGui::Selectable(data_type.data())) + { + ImGui::CloseCurrentPopup(); + scroll_to_bottom = true; + file.m_prop_manager.addEntry(Hkx::getVarTypeEnum(data_type)); + break; + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_HASHTAG)) + file.m_prop_manager.reindex(); + addTooltip("Reindex properties\nDiscard all properties marked obsolete"); + + ImGui::InputText("Filter", &m_charprop_filter), ImGui::SameLine(); + ImGui::Button(ICON_FA_QUESTION_CIRCLE); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::TextColored(g_color_invalid, "Invalid"); + ImGui::TextColored(g_color_bool, "Boolean"); + ImGui::TextColored(g_color_int, "Int8/16/32"); + ImGui::TextColored(g_color_float, "Real"); + ImGui::TextColored(g_color_attr, "Pointer"); + ImGui::TextColored(g_color_quad, "Vector/Quaternion"); + ImGui::EndTooltip(); + } + ImGui::Separator(); + + if (ImGui::BeginTable("##Charproplist", 2, table_flag, ImVec2(-FLT_MIN, -FLT_MIN))) + { + ImGui::TableSetupColumn("id", ImGuiTableColumnFlags_WidthFixed, 36); + ImGui::TableSetupColumn("name", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableNextRow(); + + auto var_list = file.m_prop_manager.getEntryList(); + std::erase_if(var_list, + [=](auto& var) { + auto disp_name = std::format("{:3} {}", var.m_index, var.get().text().as_string()); // :3 + return !(var.m_valid && hasText(disp_name, m_charprop_filter)); + }); + + ImGuiListClipper clipper; + clipper.Begin(var_list.size()); + while (clipper.Step()) + for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) + { + auto& var = var_list[row_n]; + + ImGui::TableNextColumn(); + + auto var_type = var.get().getByName("type").text().as_string(); + auto var_type_enum = Hkx::getVarTypeEnum(var_type); + if (var_type_enum < 0) + ImGui::PushStyleColor(ImGuiCol_Text, g_color_invalid); + else if (var_type_enum < 1) + ImGui::PushStyleColor(ImGuiCol_Text, g_color_bool); + else if (var_type_enum < 4) + ImGui::PushStyleColor(ImGuiCol_Text, g_color_int); + else if (var_type_enum < 5) + ImGui::PushStyleColor(ImGuiCol_Text, g_color_float); + else if (var_type_enum < 6) + ImGui::PushStyleColor(ImGuiCol_Text, g_color_attr); + else + ImGui::PushStyleColor(ImGuiCol_Text, g_color_quad); + ImGui::Text("%d", var.m_index); + addTooltip(var_type); + ImGui::PopStyleColor(); + + ImGui::TableNextColumn(); + const bool is_selected = false; + if (ImGui::Selectable(std::format("{}##{}", var.get().text().as_string(), var.m_index).c_str(), is_selected)) + { + m_charprop_current = var; + ImGui::OpenPopup("Editing Varibale"); + } + addTooltip("Click to edit"); + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + varEditPopup( + "Editing Charprop", m_charprop_current, file.m_prop_manager, [&](size_t) { return pugi::xml_node(); }, file); + + if (scroll_to_bottom) + ImGui::SetScrollHereY(1.0f); + + ImGui::EndTable(); + } + ImGui::PopID(); +} + +void VarEdit::showAnimList() +{ + ImGui::PushID("animlist"); + constexpr auto table_flag = + ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | + ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody; + + bool scroll_to_bottom = false; + auto& file = *dynamic_cast(Hkx::HkxFileManager::getSingleton()->getCurrentFile()); + auto anim_node = file.getAnimNames(); + std::vector anim_nodes = {anim_node.children().begin(), anim_node.children().end()}; + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Animation list"), ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLUS_CIRCLE)) + { + appendXmlString(anim_node, Hkx::g_def_hkStringPtr); + scroll_to_bottom = true; + } + addTooltip("Add new animation"); + + ImGui::InputText("Filter", &m_anim_filter); + ImGui::Separator(); + + pugi::xml_node mark_delete = {}; + if (ImGui::BeginTable("##Animlist", 1, table_flag, ImVec2(-FLT_MIN, -FLT_MIN))) + { + std::erase_if(anim_nodes, [&](pugi::xml_node node) { return !hasText(node.text().as_string(), m_anim_filter); }); + + ImGuiListClipper clipper; + clipper.Begin(anim_nodes.size()); + while (clipper.Step()) + for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) + { + ImGui::TableNextColumn(); + ImGui::PushID(row_n); + + std::string name = anim_nodes[row_n].text().as_string(); + ImGui::SetNextItemWidth(-1); + if (ImGui::InputText("##name", &name, ImGuiInputTextFlags_EnterReturnsTrue)) + { + anim_nodes[row_n].text() = name.c_str(); + if (name.empty()) + mark_delete = anim_nodes[row_n]; + } + addTooltip("Press Enter to apply\nRemove all characters to delete the animation entry"); + + ImGui::PopID(); + } + + if (scroll_to_bottom) + ImGui::SetScrollHereY(1.0f); + + ImGui::EndTable(); + } + if (mark_delete) + { + anim_node.remove_child(mark_delete); + anim_node.attribute("numelements") = anim_node.attribute("numelements").as_uint() - 1; + } + + ImGui::PopID(); +} } // namespace Ui } // namespace Haviour \ No newline at end of file diff --git a/src/ui/varedit.h b/src/ui/varedit.h index 56ab64c..a5cf704 100644 --- a/src/ui/varedit.h +++ b/src/ui/varedit.h @@ -26,14 +26,17 @@ class VarEdit }; int m_show_list; - std::string m_var_filter = {}, m_evt_filter = {}, m_prop_filter = {}; - Hkx::Variable m_var_current = {}; + std::string m_var_filter = {}, m_evt_filter = {}, m_prop_filter = {}, m_charprop_filter = {}, m_anim_filter = {}; + Hkx::Variable m_var_current = {}, m_charprop_current = {}; Hkx::AnimationEvent m_evt_current = {}; Hkx::CharacterProperty m_prop_current = {}; - + // behaviour file void showVarList(); void showEvtList(); void showPropList(); + // character file + void showCharPropList(); + void showAnimList(); }; } // namespace Ui } // namespace Haviour \ No newline at end of file diff --git a/src/ui/widgets.cpp b/src/ui/widgets.cpp index 4803a71..9caf3b5 100644 --- a/src/ui/widgets.cpp +++ b/src/ui/widgets.cpp @@ -10,8 +10,6 @@ namespace Haviour { namespace Ui { -#define addTooltip(...) \ - if (ImGui::IsItemHovered()) ImGui::SetTooltip(__VA_ARGS__); bool iconButton(const char* label) { @@ -346,7 +344,7 @@ void varBindingButton(const char* str_id, pugi::xml_node hkparam, Hkx::Behaviour bool ParamEdit::showButton() { varBindingButton(getName(), m_hkparam, - m_file->getType() == Hkx::HkxFile::kBehaviour ? dynamic_cast(m_file) : nullptr); + (m_file && (m_file->getType() == Hkx::HkxFile::kBehaviour)) ? dynamic_cast(m_file) : nullptr); return false; } @@ -671,7 +669,11 @@ void objLiveEditList( //////////////// Linked Prop Edits -void varEditPopup(const char* str_id, Hkx::Variable& var, Hkx::BehaviourFile& file) +void varEditPopup(const char* str_id, + Hkx::Variable& var, + Hkx::VariableManager& manager, + std::function get_ref_fn, + Hkx::HkxFile& file) { if (ImGui::BeginPopup(str_id)) { @@ -682,7 +684,7 @@ void varEditPopup(const char* str_id, Hkx::Variable& var, Hkx::BehaviourFile& fi if (ImGui::Button(ICON_FA_TRASH)) { auto name = var.get().text().as_string(); - auto ref_node = file.getFirstVarRef(var.m_index); + auto ref_node = get_ref_fn(var.m_index); if (ref_node) { spdlog::warn("This variable is still referenced by {} and potentially more object.\nID copied to clipboard.", ref_node.attribute("name").as_string()); @@ -690,7 +692,7 @@ void varEditPopup(const char* str_id, Hkx::Variable& var, Hkx::BehaviourFile& fi } else { - file.m_var_manager.delEntry(var.m_index); + manager.delEntry(var.m_index); spdlog::info("Variable {} deleted!", name); ImGui::EndTable(); ImGui::CloseCurrentPopup(); @@ -705,7 +707,7 @@ void varEditPopup(const char* str_id, Hkx::Variable& var, Hkx::BehaviourFile& fi switch (var_type_enum) { case Hkx::VARIABLE_TYPE_BOOL: - BoolEdit(var_value_node, file)(); + BoolEdit{var_value_node}(); break; case Hkx::VARIABLE_TYPE_INT8: ScalarEdit{var_value_node}(); @@ -721,19 +723,19 @@ void varEditPopup(const char* str_id, Hkx::Variable& var, Hkx::BehaviourFile& fi break; case Hkx::VARIABLE_TYPE_POINTER: ImGui::TableNextColumn(); - if (ImGui::InputText("value", &file.m_var_manager.getPointerValue(var_value_node.text().as_uint()))) + if (ImGui::InputText("value", &manager.getPointerValue(var_value_node.text().as_uint()))) file.buildRefList(getParentObj(var.get()).attribute("name").as_string()); ImGui::TableNextColumn(); break; case Hkx::VARIABLE_TYPE_VECTOR3: ImGui::TableNextColumn(); - ImGui::InputFloat3("value", file.m_var_manager.getQuadValue(var_value_node.text().as_uint()).data(), "%.6f"); + ImGui::InputFloat3("value", manager.getQuadValue(var_value_node.text().as_uint()).data(), "%.6f"); ImGui::TableNextColumn(); break; case Hkx::VARIABLE_TYPE_VECTOR4: case Hkx::VARIABLE_TYPE_QUATERNION: ImGui::TableNextColumn(); - ImGui::InputFloat4("value", file.m_var_manager.getQuadValue(var_value_node.text().as_uint()).data(), "%.6f"); + ImGui::InputFloat4("value", manager.getQuadValue(var_value_node.text().as_uint()).data(), "%.6f"); ImGui::TableNextColumn(); break; default: @@ -819,6 +821,7 @@ void propEditPopup(const char* str_id, Hkx::CharacterProperty& prop, Hkx::Behavi } addTooltip("Delete\nIf it is still referenced, the property won't delete\nand one of its referencing object will be copied to clipboard."); + EnumEdit(prop.get().getByName("type"))(); auto role_node = prop.get().getByName("role").first_child(); EnumEdit(role_node.getByName("role"))(); FlagEdit(role_node.getByName("flags"))(); diff --git a/src/ui/widgets.h b/src/ui/widgets.h index f8d4b38..e822ba1 100644 --- a/src/ui/widgets.h +++ b/src/ui/widgets.h @@ -101,13 +101,28 @@ inline void fullTableSeparator() ///////////////////////// PICKERS -// Listbox with clipping and custom item widget function -// This function returns selected index if selected -// item function should return true if selected +// ListBox with clipping, filter, max size limit and custom item widget function +// filter function returns true if item should remain +// size specifies width & item height template -std::optional pickerListBox(const char* label, ImVec2 size, std::vector& items, std::function item_func) +std::optional filteredPickerListBox(const char* label, + std::vector& items, + std::function filter_func, + std::function item_func, + ImVec2 size = {400.0f, 21.0f}, + size_t max_item = 30) { - if (ImGui::BeginListBox(label, size)) + static std::string filter_text = {}; + ImGui::InputText("Filter", &filter_text); + + std::vector items_filtered = {}; + for (auto& item : items) + if (filter_func(item, filter_text)) + items_filtered.push_back(&item - items.data()); + + if (items_filtered.empty()) + ImGui::TextDisabled("No item"); + else if (ImGui::BeginListBox(label, {size.x, size.y * std::min(items.size(), max_item)})) { ImGuiListClipper clipper; clipper.Begin(items.size()); @@ -115,10 +130,10 @@ std::optional pickerListBox(const char* label, ImVec2 size, std::vector& while (clipper.Step()) for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++) { - if (item_func(items[row_n])) + if (item_func(items[items_filtered[row_n]])) { ImGui::EndListBox(); - return row_n; + return items[items_filtered[row_n]]; } } ImGui::EndListBox(); @@ -126,29 +141,6 @@ std::optional pickerListBox(const char* label, ImVec2 size, std::vector& return std::nullopt; } -// pickerListBox + filter -// filter function returns true if item should remain -template -std::optional filteredPickerListBox(const char* label, - std::vector& items, - std::function filter_func, - std::function item_func) -{ - static std::string filter_text = {}; - ImGui::InputText("Filter", &filter_text); - - std::vector items_filtered = {}; - for (auto& item : items) - if (filter_func(item, filter_text)) - items_filtered.push_back(item); - - if (items_filtered.empty()) - ImGui::TextDisabled("No item"); - else if (auto res = pickerListBox(label, {400.0f, 21.0f * std::min(items_filtered.size(), 30ull)}, items_filtered, item_func); res.has_value()) - return items[res.value()]; - return std::nullopt; -} - template std::optional pickerPopup(const char* str_id, std::vector& items, @@ -200,14 +192,14 @@ std::optional pickerPopup(const char* str_id, template bool linkedPropSelectable(Entry& prop) { + bool res = false; if constexpr (std::is_same_v) ImGui::PushStyleColor(ImGuiCol_Text, getVarColor(prop)); - if (ImGui::Selectable(prop.getItemName().c_str())) - { - if constexpr (std::is_same_v) - ImGui::PopStyleColor(); - return true; - } + auto idstr = fmt::format("{}", prop.m_index); + ImGui::TextUnformatted(idstr.c_str()), ImGui::SameLine(); + ImGui::Dummy({std::max(0.0f, 35.0f - ImGui::CalcTextSize(idstr.c_str()).x), 0}), ImGui::SameLine(); + if (ImGui::Selectable(prop.getName())) + res = true; if constexpr (std::is_same_v) ImGui::PopStyleColor(); return false; @@ -283,14 +275,6 @@ void varBindingButton(const char* str_id, pugi::xml_node hkparam, Hkx::Behaviour //////////////// hkParam Edits -#define DEF_FACT_MEM(type, name, def_val) \ - type m_##name = def_val; \ - auto& name(type name) \ - { \ - m_##name = name; \ - return *this; \ - } - #define DEF_EDIT_CONSTR(derived, base) \ derived() = default; \ inline derived( \ @@ -631,7 +615,11 @@ void objLiveEditList( //////////////// Linked Prop Edits -void varEditPopup(const char* str_id, Hkx::Variable& var, Hkx::BehaviourFile& file); +void varEditPopup(const char* str_id, + Hkx::Variable& var, + Hkx::VariableManager& manager, + std::function get_ref_fn, + Hkx::HkxFile& file); void evtEditPopup(const char* str_id, Hkx::AnimationEvent& evt, Hkx::BehaviourFile& file); void propEditPopup(const char* str_id, Hkx::CharacterProperty& evt, Hkx::BehaviourFile& file); diff --git a/src/utils.h b/src/utils.h index 19d1f25..1aaf0d1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -118,6 +119,79 @@ template using StringMap = robin_hood::unordered_map>; using StringSet = robin_hood::unordered_set>; +// crc32 +// source: https://gist.github.com/timepp/1f678e200d9e0f2a043a9ec6b3690635 +// actually it's nemesis +inline uint32_t mirror_bit(uint32_t val, char num) +{ + uint32_t retval = 0; + for (int i = 1; i <= num; i++) + { + if (val & 1) + retval |= (1 << (num - i)); + val >>= 1; + } + return retval; +} + +struct Crc32 +{ + static inline uint32_t table[256]; + + static inline void generate_table(uint32_t (&table)[256]) + { + constexpr uint32_t polynomial = 0x04C11DB7; // pkzip + for (uint32_t i = 0; i < 256; i++) + { + // uint32_t c = i; + // for (size_t j = 0; j < 8; j++) + // { + // if (c & 1) + // c = polynomial ^ (c >> 1); + // else + // c >>= 1; + // } + // table[i] = c; + uint32_t c = mirror_bit(i, 8) << 24; + for (size_t j = 0; j < 8; j++) + c = (c << 1) ^ ((c & (1 << 31)) ? polynomial : 0); + c = mirror_bit(c, 32); + + table[i] = c; + } + } + + static inline uint32_t update(const char* buf, size_t len, uint32_t initial = 0, uint32_t finalxor = 0) + { + static std::once_flag flag; + std::call_once(flag, []() { + Crc32::generate_table(Crc32::table); + }); + + // uint32_t c = initial ^ 0xFFFFFFFF; + // const uint8_t* u = static_cast(buf); + // for (size_t i = 0; i < len; ++i) + // { + // c = table[(c ^ u[i]) & 0xFF] ^ (c >> 8); + // } + // return c ^ 0xFFFFFFFF; + + uint32_t c = initial; + while (len--) + c = (c >> 8) ^ table[(c & 0xFF) ^ *(buf++)]; + return c ^ finalxor; + } +}; + +// quickly define factory member & method +#define DEF_FACT_MEM(type, name, def_val) \ + type m_##name = def_val; \ + auto& name(type name) \ + { \ + m_##name = name; \ + return *this; \ + } + ////////////////////////// XML HELPERS inline pugi::xml_node appendXmlString(pugi::xml_node target, std::string_view srcString) {