Skip to content

Instantly share code, notes, and snippets.

@shakesoda
Last active February 17, 2021 19:56
Show Gist options
  • Save shakesoda/f5ce0c34146b208d7fb62e79128a7cfc to your computer and use it in GitHub Desktop.
Save shakesoda/f5ce0c34146b208d7fb62e79128a7cfc to your computer and use it in GitHub Desktop.
imgui for bgfx (works with unmodified imgui)
#include <imgui/imgui.h>
#include <SDL2/SDL_syswm.h>
#include "myon/imgui.hpp"
#include "myon/window.hpp"
#include "myon/timer.hpp"
#include "myon/cache.hpp"
#include "math/matrix.hpp"
#include <bgfx/bgfx.h>
#include <imgui/imgui_demo.cpp>
#include <imgui/imgui_lua_bindings.cpp>
#include "myon/graphics/views.hpp"
using namespace myon;
using namespace math;
using namespace graphics;
static bool g_MousePressed[3] = { false, false, false };
static float g_MouseWheel = 0.0f;
static double g_Time = 0.0f;
static bgfx::TextureHandle g_FontTexture;
static bgfx::UniformHandle g_AttribLocationTex;
static bgfx::VertexDecl g_imguiVertex;
void ImGui_Myon_RenderDrawLists(ImDrawData* draw_data) {
ImGuiIO& io = ImGui::GetIO();
// scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
draw_data->ScaleClipRects(io.DisplayFramebufferScale);
for (int n = 0; n < draw_data->CmdListsCount; n++) {
const ImDrawList* cmd_list = draw_data->CmdLists[n];
ImDrawIdx idx_buffer_offset = 0;
bgfx::TransientVertexBuffer vb;
if (bgfx::getAvailTransientVertexBuffer(cmd_list->VtxBuffer.Size, g_imguiVertex)) {
bgfx::allocTransientVertexBuffer(&vb, cmd_list->VtxBuffer.Size, g_imguiVertex);
memcpy(vb.data, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
}
bgfx::TransientIndexBuffer ib;
if (bgfx::getAvailTransientIndexBuffer(cmd_list->IdxBuffer.Size)) {
bgfx::allocTransientIndexBuffer(&ib, cmd_list->IdxBuffer.Size);
memcpy(ib.data, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
}
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
}
else {
bgfx::setTexture(0, g_AttribLocationTex, g_FontTexture);
bgfx::setScissor(
(uint16_t)(pcmd->ClipRect.x*window::get_dpi()),
(uint16_t)(pcmd->ClipRect.y*window::get_dpi()),
(uint16_t)(pcmd->ClipRect.z*window::get_dpi()),
(uint16_t)((pcmd->ClipRect.w - pcmd->ClipRect.y)*window::get_dpi())
);
bgfx::setVertexBuffer(&vb);
bgfx::setIndexBuffer(&ib, idx_buffer_offset, pcmd->ElemCount);
bgfx::setState(0
| BGFX_STATE_BLEND_ALPHA
| BGFX_STATE_RGB_WRITE
| BGFX_STATE_ALPHA_WRITE
| BGFX_STATE_CULL_CCW
);
bgfx::submit(VIEW_IMGUI, cache::get_shader(SHADER_INTERN_IMGUI));
}
idx_buffer_offset += pcmd->ElemCount;
}
}
}
ImGui_Myon::ImGui_Myon() {
printf("[ImGui] Initializing...\n");
ImGuiIO& io = ImGui::GetIO();
io.KeyMap[ImGuiKey_Tab] = SDLK_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
io.KeyMap[ImGuiKey_Delete] = SDLK_DELETE;
io.KeyMap[ImGuiKey_Backspace] = SDLK_BACKSPACE;
io.KeyMap[ImGuiKey_Enter] = SDLK_RETURN;
io.KeyMap[ImGuiKey_Escape] = SDLK_ESCAPE;
io.KeyMap[ImGuiKey_A] = SDLK_a;
io.KeyMap[ImGuiKey_C] = SDLK_c;
io.KeyMap[ImGuiKey_V] = SDLK_v;
io.KeyMap[ImGuiKey_X] = SDLK_x;
io.KeyMap[ImGuiKey_Y] = SDLK_y;
io.KeyMap[ImGuiKey_Z] = SDLK_z;
// Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer.
io.RenderDrawListsFn = ImGui_Myon_RenderDrawLists;
io.SetClipboardTextFn = [](void*, const char* text) {
SDL_SetClipboardText(text);
};
io.GetClipboardTextFn = [](void*) {
return (const char*)SDL_GetClipboardText();
};
io.ClipboardUserData = NULL;
#ifdef MYON_WINDOWS
SDL_Window *window = window::handle;
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(window, &wmInfo);
io.ImeWindowHandle = wmInfo.info.win.window;
#endif
// setup uniforms
g_AttribLocationTex = bgfx::createUniform("s_albedo", bgfx::UniformType::Int1);
// setup vertex format
g_imguiVertex.begin();
g_imguiVertex.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float);
g_imguiVertex.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float);
g_imguiVertex.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true);
g_imguiVertex.end();
// Load fonts
// Build texture atlas
unsigned char* pixels;
int width, height;
// Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
const bgfx::Memory *mem = bgfx::makeRef(pixels, width*height*4);
g_FontTexture = bgfx::createTexture2D(width, height, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_NONE, mem);
}
ImGui_Myon::~ImGui_Myon() {
if (g_FontTexture.idx != bgfx::invalidHandle) {
bgfx::destroyTexture(g_FontTexture);
}
ImGui::Shutdown();
}
void ImGui_Myon::wrap(lua_State *L) {
lState = L;
LoadImguiBindings();
}
void ImGui_Myon::new_frame() {
ImGuiIO& io = ImGui::GetIO();
// Setup display size every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
SDL_GetWindowSize(window::handle, &w, &h);
display_w = window::get_width();
display_h = window::get_height();
io.DisplaySize = ImVec2((float)w / window::get_dpi(), (float)h / window::get_dpi());
io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0);
// Setup orthographic projection matrix
const float ortho_projection[] = {
2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f
};
mat4 proj(ortho_projection);
// update view properties
bgfx::setViewName(VIEW_IMGUI, "ImGui");
bgfx::setViewRect(VIEW_IMGUI, 0, 0, bgfx::BackbufferRatio::Equal);
bgfx::setViewSeq(VIEW_IMGUI, true);
bgfx::setViewTransform(VIEW_IMGUI, NULL, proj.data, BGFX_VIEW_STEREO);
bgfx::touch(VIEW_IMGUI);
// Setup time step
double current_time = Timer::now();
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);
g_Time = current_time;
// Hide OS mouse cursor if ImGui is drawing it
SDL_ShowCursor(io.MouseDrawCursor ? 0 : 1);
// Setup inputs
// (we already got mouse wheel, keyboard keys & characters from SDL_PollEvent())
int mx, my;
Uint32 mouseMask = SDL_GetMouseState(&mx, &my);
// make sure not to grab mouse when it's grabbed (relative mode)
if (SDL_GetWindowFlags(window::handle) & SDL_WINDOW_MOUSE_FOCUS && !SDL_GetRelativeMouseMode()) {
// Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.)
io.MousePos = ImVec2((float)(mx / window::get_dpi()), (float)(my / window::get_dpi()));
}
else {
io.MousePos = ImVec2(-1, -1);
}
// If a mouse press event came, always pass it as "mouse held this frame",
// so we don't miss click-release events that are shorter than 1 frame.
io.MouseDown[0] = g_MousePressed[0] || (mouseMask & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
io.MouseDown[1] = g_MousePressed[1] || (mouseMask & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = g_MousePressed[2] || (mouseMask & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false;
io.MouseWheel = g_MouseWheel;
g_MouseWheel = 0.0f;
ImGui::NewFrame();
}
bool ImGui_Myon::process_event(SDL_Event* event) {
if (SDL_GetRelativeMouseMode()) {
return false;
}
ImGuiIO& io = ImGui::GetIO();
switch (event->type) {
case SDL_MOUSEWHEEL: {
if (event->wheel.y > 0) {
g_MouseWheel = 1;
}
if (event->wheel.y < 0) {
g_MouseWheel = -1;
}
return true;
}
case SDL_MOUSEBUTTONDOWN: {
if (event->button.button == SDL_BUTTON_LEFT) { g_MousePressed[0] = true; }
if (event->button.button == SDL_BUTTON_RIGHT) { g_MousePressed[1] = true; }
if (event->button.button == SDL_BUTTON_MIDDLE) { g_MousePressed[2] = true; }
return true;
}
case SDL_TEXTINPUT: {
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP: {
int key = event->key.keysym.sym & ~SDLK_SCANCODE_MASK;
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
return true;
}
}
return false;
}
bool ImGui_Myon::want_textinput() {
ImGuiIO &io = ImGui::GetIO();
return io.WantTextInput;
}
bool ImGui_Myon::want_keyboard() {
ImGuiIO &io = ImGui::GetIO();
return io.WantCaptureKeyboard;
}
bool ImGui_Myon::want_mouse() {
ImGuiIO &io = ImGui::GetIO();
return io.WantCaptureMouse;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment