diff --git a/Runtime/CMakeLists.txt b/Runtime/CMakeLists.txt index ed9e83ecc..5ee021468 100644 --- a/Runtime/CMakeLists.txt +++ b/Runtime/CMakeLists.txt @@ -218,6 +218,7 @@ endif () add_executable(metaforce CMain.cpp ${PLAT_SRCS} ImGuiConsole.hpp ImGuiConsole.cpp + ImGuiControllerConfig.hpp ImGuiControllerConfig.cpp ImGuiEntitySupport.hpp ImGuiEntitySupport.cpp) # RUNTIME_LIBRARIES repeated here for link ordering target_link_libraries(metaforce PUBLIC RuntimeCommon RuntimeCommonB ${RUNTIME_LIBRARIES} ${PLAT_LIBS}) diff --git a/Runtime/ImGuiConsole.cpp b/Runtime/ImGuiConsole.cpp index 01469b5c7..827041e82 100644 --- a/Runtime/ImGuiConsole.cpp +++ b/Runtime/ImGuiConsole.cpp @@ -1158,6 +1158,7 @@ void ImGuiConsole::ShowAppMainMenuBar(bool canInspect) { ImGui::MenuItem("Items", nullptr, &m_showItemsWindow, canInspect && m_developer && m_cheats); ImGui::MenuItem("Layers", nullptr, &m_showLayersWindow, canInspect && m_developer); ImGui::MenuItem("Console Variables", nullptr, &m_showConsoleVariablesWindow); + ImGui::MenuItem("Controller Config", nullptr, &m_controllerConfigVisible); ImGui::EndMenu(); } if (ImGui::BeginMenu("Debug")) { @@ -1292,6 +1293,7 @@ void ImGuiConsole::PreUpdate() { ShowInputViewer(); ShowPlayerTransformEditor(); ShowPipelineProgress(); + m_controllerConfig.show(m_controllerConfigVisible); } void ImGuiConsole::PostUpdate() { diff --git a/Runtime/ImGuiConsole.hpp b/Runtime/ImGuiConsole.hpp index b1e73ff5b..9a25508ac 100644 --- a/Runtime/ImGuiConsole.hpp +++ b/Runtime/ImGuiConsole.hpp @@ -7,6 +7,7 @@ #include "Runtime/World/CActor.hpp" #include "Runtime/World/CEntity.hpp" #include "Runtime/ImGuiPlayerLoadouts.hpp" +#include "Runtime/ImGuiControllerConfig.hpp" #include "Runtime/ConsoleVariables/CVarCommons.hpp" #include "Runtime/ConsoleVariables/CVarManager.hpp" @@ -100,6 +101,9 @@ private: std::string m_controllerName; u32 m_whichController = -1; + bool m_controllerConfigVisible = false; + ImGuiControllerConfig m_controllerConfig; + void ShowAppMainMenuBar(bool canInspect); void ShowMenuGame(); bool ShowEntityInfoWindow(TUniqueId uid); diff --git a/Runtime/ImGuiControllerConfig.cpp b/Runtime/ImGuiControllerConfig.cpp new file mode 100644 index 000000000..94e047908 --- /dev/null +++ b/Runtime/ImGuiControllerConfig.cpp @@ -0,0 +1,102 @@ +#include "Runtime/ImGuiControllerConfig.hpp" + +#include "aurora/pad.hpp" + +#include + +namespace metaforce { + +void ImGuiControllerConfig::show(bool& visible) { + /** TODO: + * - Implement multiple controllers + * - Implement setting controller ports + * - Implement fancy graphical UI + */ + + if (!visible) { + return; + } + + if (m_pendingMapping != nullptr) { + s32 nativeButton = PADGetNativeButtonPressed(0); + if (nativeButton != -1) { + m_pendingMapping->nativeButton = nativeButton; + m_pendingMapping = nullptr; + PADBlockInput(false); + } + } + + if (ImGui::Begin("Controller Config", &visible)) { + ImGui::Text("%s", PADGetName(0)); + u32 buttonCount = 0; + PADButtonMapping* mapping = PADGetButtonMappings(0, &buttonCount); + if (mapping != nullptr) { + for (u32 i = 0; i < buttonCount; ++i) { + bool pressed = ImGui::Button(PADGetButtonName(mapping[i].padButton)); + ImGui::SameLine(); + ImGui::Text("%s", PADGetNativeButtonName(mapping[i].nativeButton)); + + if (pressed && m_pendingMapping == nullptr) { + m_pendingMapping = &mapping[i]; + PADBlockInput(true); + } + + if (m_pendingMapping == &mapping[i]) { + ImGui::SameLine(); + ImGui::Text(" - Waiting for button..."); + } + } + } + + if (ImGui::CollapsingHeader("Dead-zones")) { + PADDeadZones* deadZones = PADGetDeadZones(0); + ImGui::Checkbox("Use Dead-zones", &deadZones->useDeadzones); + s32 tmp = deadZones->stickDeadZone; + if (ImGui::DragInt("Left Stick", &tmp)) { + deadZones->stickDeadZone = tmp; + } + tmp = deadZones->substickDeadZone; + if (ImGui::DragInt("Right Stick", &tmp)) { + deadZones->substickDeadZone = tmp; + } + ImGui::Checkbox("Emulate Triggers", &deadZones->emulateTriggers); + tmp = deadZones->leftTriggerActivationZone; + if (ImGui::DragInt("Left Trigger Activation", &tmp)) { + deadZones->leftTriggerActivationZone = tmp; + } + tmp = deadZones->rightTriggerActivationZone; + if (ImGui::DragInt("Right Trigger Activation", &tmp)) { + deadZones->rightTriggerActivationZone = tmp; + } + } + + ImGui::Separator(); + if (ImGui::Button("Display Editor")) { + m_editorVisible = true; + } + ImGui::SameLine(); + if (ImGui::Button("Save Mappings")) { + PADSerializeMappings(); + } + ImGui::SameLine(); + if (ImGui::Button("Restore Defaults")) { + for (u32 i = 0; i < 4; ++i) { + PADRestoreDefaultMapping(i); + } + } + ImGui::End(); + } + + showEditor(m_editorVisible); +} + +void ImGuiControllerConfig::showEditor(bool& visible) { + if (!visible) { + return; + } + + if (ImGui::Begin("Controller Atlas Editor", &visible)) { + ImGui::End(); + } +} +} // namespace metaforce diff --git a/Runtime/ImGuiControllerConfig.hpp b/Runtime/ImGuiControllerConfig.hpp new file mode 100644 index 000000000..26b9dfdce --- /dev/null +++ b/Runtime/ImGuiControllerConfig.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "Runtime/GCNTypes.hpp" +#include "aurora/pad.hpp" + +#include +#include +#include +#include + +namespace metaforce { +class ImGuiControllerConfig { + struct Button { + u32 button; // the SDL button this entry corresponds to + u32 uvX; // Offset if icon image in atlas from left (in pixels) + u32 uvY; // Offset if icon image in atlas from top (in pixels) + u32 width; // Width of button image (in pixels) + u32 height; // Height of button image (in pixels) + float offX; // Offset from left of config window + float offY; // Offset from top of config window + }; + + struct ControllerMapping { + std::string name; + std::pair vidPid; + std::string atlasFile; // Path to atlas relative to controller definition + std::vector