Created
May 16, 2017 22:15
-
-
Save RichardGale/6e2b74bc42b3005e08397236e4be0fd0 to your computer and use it in GitHub Desktop.
imgui+bgfx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ImGui BFFX binding | |
// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. | |
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. | |
// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). | |
// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. | |
// https://github.com/ocornut/imgui | |
#include <imgui.h> | |
#include "imgui_impl_bgfx.h" | |
// BGFX/BX | |
#include "bgfx/bgfx.h" | |
#include "bgfx/embedded_shader.h" | |
#include "bx/fpumath.h" | |
#include "bx/timer.h" | |
// Data | |
static uint8_t g_View = 255; | |
static int64_t g_Time = 0; | |
static bgfx::TextureHandle g_FontTexture = BGFX_INVALID_HANDLE; | |
static bgfx::ProgramHandle g_ShaderHandle = BGFX_INVALID_HANDLE; | |
static bgfx::UniformHandle g_AttribLocationTex = BGFX_INVALID_HANDLE; | |
static bgfx::VertexDecl g_VertexDecl; | |
// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) | |
// If text or lines are blurry when integrating ImGui in your engine: | |
// - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) | |
void ImGui_Implbgfx_RenderDrawLists(ImDrawData* draw_data) | |
{ | |
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) | |
ImGuiIO& io = ImGui::GetIO(); | |
int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); | |
int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); | |
if (fb_width == 0 || fb_height == 0) | |
return; | |
draw_data->ScaleClipRects(io.DisplayFramebufferScale); | |
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled | |
uint64_t state = BGFX_STATE_RGB_WRITE | BGFX_STATE_ALPHA_WRITE | BGFX_STATE_MSAA | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA); | |
// Setup viewport, orthographic projection matrix | |
float ortho[16]; | |
bx::mtxOrtho(ortho, 0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, 1.0f); | |
bgfx::setViewTransform(g_View, NULL, ortho); | |
bgfx::setViewRect(g_View, 0, 0, (uint16_t)fb_width, (uint16_t)fb_height); | |
// Render command lists | |
for (int n = 0; n < draw_data->CmdListsCount; n++) | |
{ | |
const ImDrawList* cmd_list = draw_data->CmdLists[n]; | |
uint32_t idx_buffer_offset = 0; | |
bgfx::TransientVertexBuffer tvb; | |
bgfx::TransientIndexBuffer tib; | |
uint32_t numVertices = (uint32_t)cmd_list->VtxBuffer.size(); | |
uint32_t numIndices = (uint32_t)cmd_list->IdxBuffer.size(); | |
if ((numVertices != bgfx::getAvailTransientVertexBuffer(numVertices, g_VertexDecl)) || | |
(numIndices != bgfx::getAvailTransientIndexBuffer(numIndices))) | |
{ | |
// not enough space in transient buffer just quit drawing the rest... | |
break; | |
} | |
bgfx::allocTransientVertexBuffer(&tvb, numVertices, g_VertexDecl); | |
bgfx::allocTransientIndexBuffer(&tib, numIndices); | |
ImDrawVert* verts = (ImDrawVert*)tvb.data; | |
memcpy(verts, cmd_list->VtxBuffer.begin(), numVertices * sizeof(ImDrawVert)); | |
ImDrawIdx* indices = (ImDrawIdx*)tib.data; | |
memcpy(indices, cmd_list->IdxBuffer.begin(), numIndices * 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 | |
{ | |
const uint16_t xx = (uint16_t)bx::fmax(pcmd->ClipRect.x, 0.0f); | |
const uint16_t yy = (uint16_t)bx::fmax(pcmd->ClipRect.y, 0.0f); | |
bgfx::setScissor(xx, yy, (uint16_t)bx::fmin(pcmd->ClipRect.z, 65535.0f)-xx, (uint16_t)bx::fmin(pcmd->ClipRect.w, 65535.0f)-yy); | |
bgfx::setState(state); | |
bgfx::TextureHandle texture = { (uint16_t)((intptr_t)pcmd->TextureId&0xffff) }; | |
bgfx::setTexture(0, g_AttribLocationTex, texture); | |
bgfx::setVertexBuffer(&tvb, 0, numVertices); | |
bgfx::setIndexBuffer(&tib, idx_buffer_offset, pcmd->ElemCount); | |
bgfx::submit(g_View, g_ShaderHandle); | |
} | |
idx_buffer_offset += pcmd->ElemCount; | |
} | |
} | |
} | |
bool ImGui_Implbgfx_CreateFontsTexture() | |
{ | |
// Build texture atlas | |
ImGuiIO& io = ImGui::GetIO(); | |
unsigned char* pixels; | |
int width, height; | |
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); | |
// Upload texture to graphics system | |
g_FontTexture = bgfx::createTexture2D((uint16_t)width, | |
(uint16_t)height, | |
false, | |
1, | |
bgfx::TextureFormat::BGRA8, | |
0, | |
bgfx::copy(pixels, width*height*4)); | |
// Store our identifier | |
io.Fonts->TexID = (void *)(intptr_t)g_FontTexture.idx; | |
return true; | |
} | |
#include "vs_imgui.bin.h" | |
#include "fs_imgui.bin.h" | |
static const bgfx::EmbeddedShader s_embeddedShaders[] = | |
{ | |
BGFX_EMBEDDED_SHADER(vs_ocornut_imgui), | |
BGFX_EMBEDDED_SHADER(fs_ocornut_imgui), | |
BGFX_EMBEDDED_SHADER_END() | |
}; | |
bool ImGui_Implbgfx_CreateDeviceObjects() | |
{ | |
bgfx::RendererType::Enum type = bgfx::getRendererType(); | |
g_ShaderHandle = bgfx::createProgram(bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_ocornut_imgui"), | |
bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_ocornut_imgui"), | |
true); | |
g_VertexDecl.begin() | |
.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float) | |
.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float) | |
.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true) | |
.end(); | |
g_AttribLocationTex = bgfx::createUniform("g_AttribLocationTex", bgfx::UniformType::Int1); | |
ImGui_Implbgfx_CreateFontsTexture(); | |
return true; | |
} | |
void ImGui_Implbgfx_InvalidateDeviceObjects() | |
{ | |
bgfx::destroyUniform(g_AttribLocationTex); | |
bgfx::destroyProgram(g_ShaderHandle); | |
if (isValid(g_FontTexture)) | |
{ | |
bgfx::destroyTexture(g_FontTexture); | |
ImGui::GetIO().Fonts->TexID = 0; | |
g_FontTexture.idx = bgfx::invalidHandle; | |
} | |
} | |
void ImGui_Implbgfx_Init(int view) | |
{ | |
g_View = (uint8_t)(view&0xff); | |
ImGuiIO& io = ImGui::GetIO(); | |
// io.KeyMap[ImGuiKey_Tab] = (int)entry::Key::Tab; | |
// io.KeyMap[ImGuiKey_LeftArrow] = (int)entry::Key::Left; | |
// io.KeyMap[ImGuiKey_RightArrow] = (int)entry::Key::Right; | |
// io.KeyMap[ImGuiKey_UpArrow] = (int)entry::Key::Up; | |
// io.KeyMap[ImGuiKey_DownArrow] = (int)entry::Key::Down; | |
// io.KeyMap[ImGuiKey_Home] = (int)entry::Key::Home; | |
// io.KeyMap[ImGuiKey_End] = (int)entry::Key::End; | |
// io.KeyMap[ImGuiKey_Delete] = (int)entry::Key::Delete; | |
// io.KeyMap[ImGuiKey_Backspace] = (int)entry::Key::Backspace; | |
// io.KeyMap[ImGuiKey_Enter] = (int)entry::Key::Return; | |
// io.KeyMap[ImGuiKey_Escape] = (int)entry::Key::Esc; | |
// io.KeyMap[ImGuiKey_A] = (int)entry::Key::KeyA; | |
// io.KeyMap[ImGuiKey_C] = (int)entry::Key::KeyC; | |
// io.KeyMap[ImGuiKey_V] = (int)entry::Key::KeyV; | |
// io.KeyMap[ImGuiKey_X] = (int)entry::Key::KeyX; | |
// io.KeyMap[ImGuiKey_Y] = (int)entry::Key::KeyY; | |
// io.KeyMap[ImGuiKey_Z] = (int)entry::Key::KeyZ; | |
io.RenderDrawListsFn = ImGui_Implbgfx_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer. | |
g_Time = bx::getHPCounter(); | |
} | |
void ImGui_Implbgfx_Shutdown() | |
{ | |
ImGui_Implbgfx_InvalidateDeviceObjects(); | |
ImGui::Shutdown(); | |
} | |
void ImGui_Implbgfx_NewFrame(float width, | |
float height, | |
float mouse_x, | |
float mouse_y, | |
bool mousePressed0, | |
bool mousePressed1, | |
bool mousePressed2, | |
float mouseWheel, | |
int key) | |
{ | |
if (!isValid(g_FontTexture)) | |
ImGui_Implbgfx_CreateDeviceObjects(); | |
ImGuiIO& io = ImGui::GetIO(); | |
// Setup display size (every frame to accommodate for window resizing) | |
io.DisplaySize = ImVec2(width, height); | |
// Setup time step | |
int64_t current_time = bx::getHPCounter(); | |
const double freq = (double)bx::getHPFrequency(); | |
io.DeltaTime = (float)((current_time - g_Time)/freq); | |
g_Time = current_time; | |
// Setup inputs | |
io.MousePos = ImVec2(mouse_x, mouse_y); | |
io.MouseDown[0] = mousePressed0; | |
io.MouseDown[1] = mousePressed1; | |
io.MouseDown[2] = mousePressed2; | |
io.MouseWheel = mouseWheel; | |
if (key < 0x7f) | |
{ | |
io.AddInputCharacter(key); | |
} | |
// Start the frame | |
ImGui::NewFrame(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ImGui BGFX binding | |
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. | |
// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). | |
// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. | |
// https://github.com/ocornut/imgui | |
void ImGui_Implbgfx_Init(int view); | |
void ImGui_Implbgfx_Shutdown(); | |
void ImGui_Implbgfx_NewFrame(float width, | |
float height, | |
float mouse_x, | |
float mouse_y, | |
bool mousePressed0, | |
bool mousePressed1, | |
bool mousePressed2, | |
float mouseWheel, | |
int key); | |
// Use if you want to reset your rendering device without losing ImGui state. | |
void ImGui_Implbgfx_InvalidateDeviceObjects(); | |
bool ImGui_Implbgfx_CreateDeviceObjects(); |
Since v1.86, according to https://github.com/ocornut/imgui/releases/tag/v1.86 if you get visual glitches when using popups change this line L93 to
bgfx::setIndexBuffer(&tib, pcmd->IdxOffset, pcmd->ElemCount);
I just tested this locally and you're 100% correct. I could see the visual glitch before and then saw it disappear after. I've updated my fork of this Gist here with the changes. Thanks for reporting it! 😄
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @RichardGale,
I found this Gist super helpful getting setup with bgfx and Dear ImGui. Thank you very much for sharing it!
As both bgfx and Dear ImGui have undergone some API updates since this was first posted I've created a fork with a bunch of small fixes to get things compiling and running today - https://gist.github.com/pr0g/aff79b71bf9804ddb03f39ca7c0c3bbb
The first revision I pushed includes the most minimal subset of changes to get things working again, I will likely continue to update it more in future.
(Update 2020/11/01: I've updated the Gist again with a small set of changes to have this
bgfx
implementation work with the latestDear ImGui
changes - ocornut/imgui@0f13fdd).Thanks again!
Tom