iwa/source/addons/imgui/addon.cpp
2024-04-06 14:11:26 +02:00

300 lines
11 KiB
C++

#include "iwa/addons/imgui/addon.hpp"
#include <imgui.h>
#include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_vulkan.h>
namespace iwa
{
namespace
{
ImGuiAddon gImguiAddon;
PFN_vkVoidFunction imguiLoaderCallback(const char* functionName, void* userData)
{
return static_cast<Instance*>(userData)->getVkHandle().getProcAddr(functionName);
}
ImGuiKey keyToImGui(const KeyCode keyCode)
{
switch (keyCode)
{
//<editor-fold desc="Many cases">
case KeyCode::TAB: return ImGuiKey_Tab;
case KeyCode::LEFT: return ImGuiKey_LeftArrow;
case KeyCode::RIGHT: return ImGuiKey_RightArrow;
case KeyCode::UP: return ImGuiKey_UpArrow;
case KeyCode::DOWN: return ImGuiKey_DownArrow;
case KeyCode::PAGEUP: return ImGuiKey_PageUp;
case KeyCode::PAGEDOWN: return ImGuiKey_PageDown;
case KeyCode::HOME: return ImGuiKey_Home;
case KeyCode::END: return ImGuiKey_End;
case KeyCode::INSERT: return ImGuiKey_Insert;
case KeyCode::DELETE: return ImGuiKey_Delete;
case KeyCode::BACKSPACE: return ImGuiKey_Backspace;
case KeyCode::SPACE: return ImGuiKey_Space;
case KeyCode::RETURN: return ImGuiKey_Enter;
case KeyCode::ESCAPE: return ImGuiKey_Escape;
case KeyCode::QUOTE: return ImGuiKey_Apostrophe;
case KeyCode::COMMA: return ImGuiKey_Comma;
case KeyCode::MINUS: return ImGuiKey_Minus;
case KeyCode::PERIOD: return ImGuiKey_Period;
case KeyCode::SLASH: return ImGuiKey_Slash;
case KeyCode::SEMICOLON: return ImGuiKey_Semicolon;
case KeyCode::EQUALS: return ImGuiKey_Equal;
case KeyCode::LEFTBRACKET: return ImGuiKey_LeftBracket;
case KeyCode::BACKSLASH: return ImGuiKey_Backslash;
case KeyCode::RIGHTBRACKET: return ImGuiKey_RightBracket;
case KeyCode::BACKQUOTE: return ImGuiKey_GraveAccent;
case KeyCode::CAPSLOCK: return ImGuiKey_CapsLock;
case KeyCode::SCROLLLOCK: return ImGuiKey_ScrollLock;
case KeyCode::NUMLOCKCLEAR: return ImGuiKey_NumLock;
case KeyCode::PRINTSCREEN: return ImGuiKey_PrintScreen;
case KeyCode::PAUSE: return ImGuiKey_Pause;
case KeyCode::KP_0: return ImGuiKey_Keypad0;
case KeyCode::KP_1: return ImGuiKey_Keypad1;
case KeyCode::KP_2: return ImGuiKey_Keypad2;
case KeyCode::KP_3: return ImGuiKey_Keypad3;
case KeyCode::KP_4: return ImGuiKey_Keypad4;
case KeyCode::KP_5: return ImGuiKey_Keypad5;
case KeyCode::KP_6: return ImGuiKey_Keypad6;
case KeyCode::KP_7: return ImGuiKey_Keypad7;
case KeyCode::KP_8: return ImGuiKey_Keypad8;
case KeyCode::KP_9: return ImGuiKey_Keypad9;
case KeyCode::KP_PERIOD: return ImGuiKey_KeypadDecimal;
case KeyCode::KP_DIVIDE: return ImGuiKey_KeypadDivide;
case KeyCode::KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
case KeyCode::KP_MINUS: return ImGuiKey_KeypadSubtract;
case KeyCode::KP_PLUS: return ImGuiKey_KeypadAdd;
case KeyCode::KP_ENTER: return ImGuiKey_KeypadEnter;
case KeyCode::KP_EQUALS: return ImGuiKey_KeypadEqual;
case KeyCode::LCTRL: return ImGuiKey_LeftCtrl;
case KeyCode::LSHIFT: return ImGuiKey_LeftShift;
case KeyCode::LALT: return ImGuiKey_LeftAlt;
case KeyCode::LGUI: return ImGuiKey_LeftSuper;
case KeyCode::RCTRL: return ImGuiKey_RightCtrl;
case KeyCode::RSHIFT: return ImGuiKey_RightShift;
case KeyCode::RALT: return ImGuiKey_RightAlt;
case KeyCode::RGUI: return ImGuiKey_RightSuper;
case KeyCode::APPLICATION: return ImGuiKey_Menu;
case KeyCode::_0: return ImGuiKey_0;
case KeyCode::_1: return ImGuiKey_1;
case KeyCode::_2: return ImGuiKey_2;
case KeyCode::_3: return ImGuiKey_3;
case KeyCode::_4: return ImGuiKey_4;
case KeyCode::_5: return ImGuiKey_5;
case KeyCode::_6: return ImGuiKey_6;
case KeyCode::_7: return ImGuiKey_7;
case KeyCode::_8: return ImGuiKey_8;
case KeyCode::_9: return ImGuiKey_9;
case KeyCode::A: return ImGuiKey_A;
case KeyCode::B: return ImGuiKey_B;
case KeyCode::C: return ImGuiKey_C;
case KeyCode::D: return ImGuiKey_D;
case KeyCode::E: return ImGuiKey_E;
case KeyCode::F: return ImGuiKey_F;
case KeyCode::G: return ImGuiKey_G;
case KeyCode::H: return ImGuiKey_H;
case KeyCode::I: return ImGuiKey_I;
case KeyCode::J: return ImGuiKey_J;
case KeyCode::K: return ImGuiKey_K;
case KeyCode::L: return ImGuiKey_L;
case KeyCode::M: return ImGuiKey_M;
case KeyCode::N: return ImGuiKey_N;
case KeyCode::O: return ImGuiKey_O;
case KeyCode::P: return ImGuiKey_P;
case KeyCode::Q: return ImGuiKey_Q;
case KeyCode::R: return ImGuiKey_R;
case KeyCode::S: return ImGuiKey_S;
case KeyCode::T: return ImGuiKey_T;
case KeyCode::U: return ImGuiKey_U;
case KeyCode::V: return ImGuiKey_V;
case KeyCode::W: return ImGuiKey_W;
case KeyCode::X: return ImGuiKey_X;
case KeyCode::Y: return ImGuiKey_Y;
case KeyCode::Z: return ImGuiKey_Z;
case KeyCode::F1: return ImGuiKey_F1;
case KeyCode::F2: return ImGuiKey_F2;
case KeyCode::F3: return ImGuiKey_F3;
case KeyCode::F4: return ImGuiKey_F4;
case KeyCode::F5: return ImGuiKey_F5;
case KeyCode::F6: return ImGuiKey_F6;
case KeyCode::F7: return ImGuiKey_F7;
case KeyCode::F8: return ImGuiKey_F8;
case KeyCode::F9: return ImGuiKey_F9;
case KeyCode::F10: return ImGuiKey_F10;
case KeyCode::F11: return ImGuiKey_F11;
case KeyCode::F12: return ImGuiKey_F12;
case KeyCode::F13: return ImGuiKey_F13;
case KeyCode::F14: return ImGuiKey_F14;
case KeyCode::F15: return ImGuiKey_F15;
case KeyCode::F16: return ImGuiKey_F16;
case KeyCode::F17: return ImGuiKey_F17;
case KeyCode::F18: return ImGuiKey_F18;
case KeyCode::F19: return ImGuiKey_F19;
case KeyCode::F20: return ImGuiKey_F20;
case KeyCode::F21: return ImGuiKey_F21;
case KeyCode::F22: return ImGuiKey_F22;
case KeyCode::F23: return ImGuiKey_F23;
case KeyCode::F24: return ImGuiKey_F24;
case KeyCode::AC_BACK: return ImGuiKey_AppBack;
case KeyCode::AC_FORWARD: return ImGuiKey_AppForward;
default: return ImGuiKey_None;
//</editor-fold>
}
}
ImGuiMouseButton mouseButtonToImGui(const MouseButton button)
{
switch (button)
{
case MouseButton::LEFT:
return ImGuiMouseButton_Left;
case MouseButton::RIGHT:
return ImGuiMouseButton_Right;
case MouseButton::MIDDLE:
return ImGuiMouseButton_Middle;
case MouseButton::EXTRA_1:
return ImGuiMouseButton_Middle + 1;
case MouseButton::EXTRA_2:
return ImGuiMouseButton_Middle + 2;
}
return -1;
}
}
void ImGuiAddon::init(const AddonInitArgs& args)
{
(void) args;
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui::StyleColorsDark();
}
void ImGuiAddon::cleanup()
{
ImGui::DestroyContext();
}
mijin::Task<> ImGuiAddon::c_createResources(const ImguiCreateResourcesArgs& args)
{
const unsigned QUEUED_FRAMES = 3; // TODO: is this okay?
const unsigned MAX_IMAGES = 256;
Device& device = *args.swapchain.getOwner();
mDescriptorPool = device.createChild<DescriptorPool>(DescriptorPoolCreationArgs{
.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
.maxSets = MAX_IMAGES,
.poolSizes = {
vk::DescriptorPoolSize{.type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = MAX_IMAGES}
}
});
registerObjectDestructionHandler(args.swapchain, [this, device = device.getPointer()]
{
// destroy the descriptor pool when the window is destroyed so we don't keep the device alive
device->getVkHandle().waitIdle();
ImGui_ImplVulkan_Shutdown();
ImGui_ImplSDL2_Shutdown();
mDescriptorPool = nullptr;
mWidgets.clear();
});
if (!ImGui_ImplSDL2_InitForVulkan(args.swapchain.getWindow()->getSDLWindow())) {
throw std::runtime_error("Error initializing ImGui for SDL2.");
}
ImGui_ImplVulkan_InitInfo initInfo{
.Instance = device.getOwner()->getVkHandle(),
.PhysicalDevice = device.getVkPhysicalDevice(),
.Device = device.getVkHandle(),
.QueueFamily = device.getDeviceInfo().graphicsQueueFamily,
.Queue = device.getGraphicsQueue(),
.PipelineCache = VK_NULL_HANDLE,
.DescriptorPool = mDescriptorPool->getVkHandle(),
.Subpass = 0,
.MinImageCount = QUEUED_FRAMES,
.ImageCount = QUEUED_FRAMES,
.MSAASamples = VK_SAMPLE_COUNT_1_BIT,
.UseDynamicRendering = true,
.ColorAttachmentFormat = static_cast<VkFormat>(args.format),
.Allocator = nullptr,
.CheckVkResultFn = nullptr,
};
const bool success = ImGui_ImplVulkan_LoadFunctions(imguiLoaderCallback, device.getOwner())
&& ImGui_ImplVulkan_Init(&initInfo, VK_NULL_HANDLE);
if (!success) {
throw std::runtime_error("Error initializing ImGui for Vulkan.");
}
// setup input
args.swapchain.getWindow()->keyChanged.connect(*this, &ImGuiAddon::handleKeyChanged);
args.swapchain.getWindow()->mouseButtonChanged.connect(*this, &ImGuiAddon::handleMouseButtonChanged);
args.swapchain.getWindow()->mouseMoved.connect(*this, &ImGuiAddon::handleMouseMoved);
args.swapchain.getWindow()->mouseScrolled.connect(*this, &ImGuiAddon::handleMouseScrolled);
args.swapchain.getWindow()->textEntered.connect(*this, &ImGuiAddon::handleTextEntered);
// first frame
beginFrame();
co_return;
}
void ImGuiAddon::renderFrame(vk::CommandBuffer cmdBuffer)
{
for (const std::unique_ptr<ImGuiWidget>& widget : mWidgets) {
widget->draw();
}
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmdBuffer);
beginFrame();
}
void ImGuiAddon::removeWidget(ImGuiWidget* widget)
{
auto it = std::ranges::find_if(mWidgets, [widget](const std::unique_ptr<ImGuiWidget>& widgetPtr)
{
return widgetPtr.get() == widget;
});
mWidgets.erase(it);
}
void ImGuiAddon::beginFrame() noexcept
{
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
}
void ImGuiAddon::handleKeyChanged(const KeyEvent& event)
{
ImGui::GetIO().AddKeyEvent(keyToImGui(event.keyCode), event.down);
}
void ImGuiAddon::handleMouseButtonChanged(const MouseButtonEvent& event)
{
ImGui::GetIO().AddMouseButtonEvent(mouseButtonToImGui(event.button), event.down);
}
void ImGuiAddon::handleMouseMoved(const MouseMoveEvent& event)
{
ImGui::GetIO().AddMousePosEvent(static_cast<float>(event.absoluteX), static_cast<float>(event.absoluteY));
}
void ImGuiAddon::handleMouseScrolled(const MouseWheelEvent& event)
{
ImGui::GetIO().AddMouseWheelEvent(static_cast<float>(event.relativeX), static_cast<float>(event.relativeY));
}
void ImGuiAddon::handleTextEntered(const TextInputEvent& event)
{
ImGui::GetIO().AddInputCharactersUTF8(event.text.c_str());
}
ImGuiAddon& ImGuiAddon::get() noexcept
{
return gImguiAddon;
}
} // namespace iwa