/********************************************************************************************** * * raylibExtras * Utilities and Shared Components for Raylib * * rlImGui * basic ImGui integration * * LICENSE: ZLIB * * Copyright (c) 2020 Jeffery Myers * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * **********************************************************************************************/ #include "rlImGui.h" #include "imgui.h" #include "raylib.h" #include "rlgl.h" #ifdef PLATFORM_DESKTOP #include #endif #include #ifndef NO_FONT_AWESOME #include "extras/FA6FreeSolidFontData.h" #endif static Texture2D FontTexture; static ImGuiMouseCursor CurrentMouseCursor = ImGuiMouseCursor_COUNT; static MouseCursor MouseCursorMap[ImGuiMouseCursor_COUNT]; static const char* rlImGuiGetClipText(void*) { return GetClipboardText(); } static void rlImGuiSetClipText(void*, const char* text) { SetClipboardText(text); } static void rlImGuiNewFrame() { ImGuiIO& io = ImGui::GetIO(); if (IsWindowFullscreen()) { int monitor = GetCurrentMonitor(); io.DisplaySize.x = float(GetMonitorWidth(monitor)); io.DisplaySize.y = float(GetMonitorHeight(monitor)); } else { io.DisplaySize.x = float(GetScreenWidth()); io.DisplaySize.y = float(GetScreenHeight()); } int width = int(io.DisplaySize.x), height = int(io.DisplaySize.y); #ifdef PLATFORM_DESKTOP glfwGetFramebufferSize(glfwGetCurrentContext(), &width, &height); #endif if (width > 0 && height > 0) { io.DisplayFramebufferScale = ImVec2(width / io.DisplaySize.x, height / io.DisplaySize.y); } else { io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); } io.DeltaTime = GetFrameTime(); io.KeyCtrl = IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_CONTROL); io.KeyShift = IsKeyDown(KEY_RIGHT_SHIFT) || IsKeyDown(KEY_LEFT_SHIFT); io.KeyAlt = IsKeyDown(KEY_RIGHT_ALT) || IsKeyDown(KEY_LEFT_ALT); io.KeySuper = IsKeyDown(KEY_RIGHT_SUPER) || IsKeyDown(KEY_LEFT_SUPER); if (io.WantSetMousePos) { SetMousePosition((int)io.MousePos.x, (int)io.MousePos.y); } else { io.MousePos.x = (float)GetMouseX(); io.MousePos.y = (float)GetMouseY(); } io.MouseDown[0] = IsMouseButtonDown(MOUSE_LEFT_BUTTON); io.MouseDown[1] = IsMouseButtonDown(MOUSE_RIGHT_BUTTON); io.MouseDown[2] = IsMouseButtonDown(MOUSE_MIDDLE_BUTTON); if (GetMouseWheelMove() > 0) io.MouseWheel += 1; else if (GetMouseWheelMove() < 0) io.MouseWheel -= 1; if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0) { ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (imgui_cursor != CurrentMouseCursor || io.MouseDrawCursor) { CurrentMouseCursor = imgui_cursor; if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) { HideCursor(); } else { ShowCursor(); if (!(io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)) { SetMouseCursor((imgui_cursor > -1 && imgui_cursor < ImGuiMouseCursor_COUNT) ? MouseCursorMap[imgui_cursor] : MOUSE_CURSOR_DEFAULT); } } } } } #define FOR_ALL_KEYS(X) \ do { \ X(KEY_APOSTROPHE); \ X(KEY_COMMA); \ X(KEY_MINUS); \ X(KEY_PERIOD); \ X(KEY_SLASH); \ X(KEY_ZERO); \ X(KEY_ONE); \ X(KEY_TWO); \ X(KEY_THREE); \ X(KEY_FOUR); \ X(KEY_FIVE); \ X(KEY_SIX); \ X(KEY_SEVEN); \ X(KEY_EIGHT); \ X(KEY_NINE); \ X(KEY_SEMICOLON); \ X(KEY_EQUAL); \ X(KEY_A); \ X(KEY_B); \ X(KEY_C); \ X(KEY_D); \ X(KEY_E); \ X(KEY_F); \ X(KEY_G); \ X(KEY_H); \ X(KEY_I); \ X(KEY_J); \ X(KEY_K); \ X(KEY_L); \ X(KEY_M); \ X(KEY_N); \ X(KEY_O); \ X(KEY_P); \ X(KEY_Q); \ X(KEY_R); \ X(KEY_S); \ X(KEY_T); \ X(KEY_U); \ X(KEY_V); \ X(KEY_W); \ X(KEY_X); \ X(KEY_Y); \ X(KEY_Z); \ X(KEY_SPACE); \ X(KEY_ESCAPE); \ X(KEY_ENTER); \ X(KEY_TAB); \ X(KEY_BACKSPACE); \ X(KEY_INSERT); \ X(KEY_DELETE); \ X(KEY_RIGHT); \ X(KEY_LEFT); \ X(KEY_DOWN); \ X(KEY_UP); \ X(KEY_PAGE_UP); \ X(KEY_PAGE_DOWN); \ X(KEY_HOME); \ X(KEY_END); \ X(KEY_CAPS_LOCK); \ X(KEY_SCROLL_LOCK); \ X(KEY_NUM_LOCK); \ X(KEY_PRINT_SCREEN); \ X(KEY_PAUSE); \ X(KEY_F1); \ X(KEY_F2); \ X(KEY_F3); \ X(KEY_F4); \ X(KEY_F5); \ X(KEY_F6); \ X(KEY_F7); \ X(KEY_F8); \ X(KEY_F9); \ X(KEY_F10); \ X(KEY_F11); \ X(KEY_F12); \ X(KEY_LEFT_SHIFT); \ X(KEY_LEFT_CONTROL); \ X(KEY_LEFT_ALT); \ X(KEY_LEFT_SUPER); \ X(KEY_RIGHT_SHIFT); \ X(KEY_RIGHT_CONTROL); \ X(KEY_RIGHT_ALT); \ X(KEY_RIGHT_SUPER); \ X(KEY_KB_MENU); \ X(KEY_LEFT_BRACKET); \ X(KEY_BACKSLASH); \ X(KEY_RIGHT_BRACKET); \ X(KEY_GRAVE); \ X(KEY_KP_0); \ X(KEY_KP_1); \ X(KEY_KP_2); \ X(KEY_KP_3); \ X(KEY_KP_4); \ X(KEY_KP_5); \ X(KEY_KP_6); \ X(KEY_KP_7); \ X(KEY_KP_8); \ X(KEY_KP_9); \ X(KEY_KP_DECIMAL); \ X(KEY_KP_DIVIDE); \ X(KEY_KP_MULTIPLY); \ X(KEY_KP_SUBTRACT); \ X(KEY_KP_ADD); \ X(KEY_KP_ENTER); \ X(KEY_KP_EQUAL); \ } while(0) #define SET_KEY_DOWN(KEY) io.KeysDown[KEY] = IsKeyDown(KEY) static void rlImGuiEvents() { ImGuiIO& io = ImGui::GetIO(); FOR_ALL_KEYS(SET_KEY_DOWN); unsigned int pressed = GetCharPressed(); if (pressed != 0) io.AddInputCharacter(pressed); } static void rlImGuiTriangleVert(ImDrawVert& idx_vert) { Color* c; c = (Color*)&idx_vert.col; rlColor4ub(c->r, c->g, c->b, c->a); rlTexCoord2f(idx_vert.uv.x, idx_vert.uv.y); rlVertex2f(idx_vert.pos.x, idx_vert.pos.y); } static void rlImGuiRenderTriangles(unsigned int count, int indexStart, const ImVector& indexBuffer, const ImVector& vertBuffer, void* texturePtr) { if (count < 3) return; Texture* texture = (Texture*)texturePtr; unsigned int textureId = (texture == nullptr) ? 0 : texture->id; rlBegin(RL_TRIANGLES); rlSetTexture(textureId); for (unsigned int i = 0; i <= (count - 3); i += 3) { if(rlCheckRenderBatchLimit(3)) { rlBegin(RL_TRIANGLES); rlSetTexture(textureId); } ImDrawIdx indexA = indexBuffer[indexStart + i]; ImDrawIdx indexB = indexBuffer[indexStart + i + 1]; ImDrawIdx indexC = indexBuffer[indexStart + i + 2]; ImDrawVert vertexA = vertBuffer[indexA]; ImDrawVert vertexB = vertBuffer[indexB]; ImDrawVert vertexC = vertBuffer[indexC]; rlImGuiTriangleVert(vertexA); rlImGuiTriangleVert(vertexB); rlImGuiTriangleVert(vertexC); } rlEnd(); } static void EnableScissor(float x, float y, float width, float height) { rlEnableScissorTest(); ImGuiIO& io = ImGui::GetIO(); rlScissor((int)(x * io.DisplayFramebufferScale.x), int((GetScreenHeight() - (int)(y + height)) * io.DisplayFramebufferScale.y), (int)(width * io.DisplayFramebufferScale.x), (int)(height * io.DisplayFramebufferScale.y)); } static void rlRenderData(ImDrawData* data) { rlDrawRenderBatchActive(); rlDisableBackfaceCulling(); for (int l = 0; l < data->CmdListsCount; ++l) { const ImDrawList* commandList = data->CmdLists[l]; for (const auto& cmd : commandList->CmdBuffer) { EnableScissor(cmd.ClipRect.x - data->DisplayPos.x, cmd.ClipRect.y - data->DisplayPos.y, cmd.ClipRect.z - (cmd.ClipRect.x - data->DisplayPos.x), cmd.ClipRect.w - (cmd.ClipRect.y - data->DisplayPos.y)); if (cmd.UserCallback != nullptr) { cmd.UserCallback(commandList, &cmd); continue; } rlImGuiRenderTriangles(cmd.ElemCount, cmd.IdxOffset, commandList->IdxBuffer, commandList->VtxBuffer, cmd.TextureId); rlDrawRenderBatchActive(); } } rlSetTexture(0); rlDisableScissorTest(); rlEnableBackfaceCulling(); } void SetupMouseCursors() { MouseCursorMap[ImGuiMouseCursor_Arrow] = MOUSE_CURSOR_ARROW; MouseCursorMap[ImGuiMouseCursor_TextInput] = MOUSE_CURSOR_IBEAM; MouseCursorMap[ImGuiMouseCursor_Hand] = MOUSE_CURSOR_POINTING_HAND; MouseCursorMap[ImGuiMouseCursor_ResizeAll] = MOUSE_CURSOR_RESIZE_ALL; MouseCursorMap[ImGuiMouseCursor_ResizeEW] = MOUSE_CURSOR_RESIZE_EW; MouseCursorMap[ImGuiMouseCursor_ResizeNESW] = MOUSE_CURSOR_RESIZE_NESW; MouseCursorMap[ImGuiMouseCursor_ResizeNS] = MOUSE_CURSOR_RESIZE_NS; MouseCursorMap[ImGuiMouseCursor_ResizeNWSE] = MOUSE_CURSOR_RESIZE_NWSE; MouseCursorMap[ImGuiMouseCursor_NotAllowed] = MOUSE_CURSOR_NOT_ALLOWED; } void rlImGuiBeginInitImGui() { ImGui::CreateContext(nullptr); } void rlImGuiEndInitImGui() { SetupMouseCursors(); ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = "imgui_impl_raylib"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; io.KeyMap[ImGuiKey_Tab] = KEY_TAB; io.KeyMap[ImGuiKey_LeftArrow] = KEY_LEFT; io.KeyMap[ImGuiKey_RightArrow] = KEY_RIGHT; io.KeyMap[ImGuiKey_UpArrow] = KEY_UP; io.KeyMap[ImGuiKey_DownArrow] = KEY_DOWN; io.KeyMap[ImGuiKey_PageUp] = KEY_PAGE_DOWN; io.KeyMap[ImGuiKey_PageDown] = KEY_PAGE_UP; io.KeyMap[ImGuiKey_Home] = KEY_HOME; io.KeyMap[ImGuiKey_End] = KEY_END; io.KeyMap[ImGuiKey_Insert] = KEY_INSERT; io.KeyMap[ImGuiKey_Delete] = KEY_DELETE; io.KeyMap[ImGuiKey_Backspace] = KEY_BACKSPACE; io.KeyMap[ImGuiKey_Space] = KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = KEY_ESCAPE; io.KeyMap[ImGuiKey_KeyPadEnter] = KEY_KP_ENTER; io.KeyMap[ImGuiKey_A] = KEY_A; io.KeyMap[ImGuiKey_C] = KEY_C; io.KeyMap[ImGuiKey_V] = KEY_V; io.KeyMap[ImGuiKey_X] = KEY_X; io.KeyMap[ImGuiKey_Y] = KEY_Y; io.KeyMap[ImGuiKey_Z] = KEY_Z; io.MousePos = ImVec2(0, 0); io.SetClipboardTextFn = rlImGuiSetClipText; io.GetClipboardTextFn = rlImGuiGetClipText; io.ClipboardUserData = nullptr; rlImGuiReloadFonts(); } void rlImGuiSetup(bool dark) { rlImGuiBeginInitImGui(); if (dark) ImGui::StyleColorsDark(); else ImGui::StyleColorsLight(); ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontDefault(); #ifndef NO_FONT_AWESOME static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig icons_config; icons_config.MergeMode = true; icons_config.PixelSnapH = true; icons_config.FontDataOwnedByAtlas = false; io.Fonts->AddFontFromMemoryCompressedTTF((void*)fa_solid_900_compressed_data, fa_solid_900_compressed_size, FONT_AWESOME_ICON_SIZE, &icons_config, icons_ranges); #endif rlImGuiEndInitImGui(); } void rlImGuiReloadFonts() { ImGuiIO& io = ImGui::GetIO(); unsigned char* pixels = nullptr; int width; int height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, nullptr); Image image = GenImageColor(width, height, BLANK); memcpy(image.data, pixels, width * height * 4); if (FontTexture.id != 0) UnloadTexture(FontTexture); FontTexture = LoadTextureFromImage(image); UnloadImage(image); io.Fonts->TexID = &FontTexture; } void rlImGuiBegin() { rlImGuiNewFrame(); rlImGuiEvents(); ImGui::NewFrame(); } void rlImGuiEnd() { ImGui::Render(); rlRenderData(ImGui::GetDrawData()); } void rlImGuiShutdown() { UnloadTexture(FontTexture); ImGui::DestroyContext(); } void rlImGuiImage(const Texture *image) { ImGui::Image((ImTextureID)image, ImVec2(float(image->width), float(image->height))); } bool rlImGuiImageButton(const Texture *image) { return ImGui::ImageButton((ImTextureID)image, ImVec2(float(image->width), float(image->height))); } void rlImGuiImageSize(const Texture *image, int width, int height) { ImGui::Image((ImTextureID)image, ImVec2(float(width), float(height))); } void rlImGuiImageRect(const Texture* image, int destWidth, int destHeight, Rectangle sourceRect) { ImVec2 uv0; ImVec2 uv1; if (sourceRect.width < 0) { uv0.x = -((float)sourceRect.x / image->width); uv1.x = (uv0.x - (float)(fabs(sourceRect.width) / image->width)); } else { uv0.x = (float)sourceRect.x / image->width; uv1.x = uv0.x + (float)(sourceRect.width / image->width); } if (sourceRect.height < 0) { uv0.y = -((float)sourceRect.y / image->height); uv1.y = (uv0.y - (float)(fabs(sourceRect.height) / image->height)); } else { uv0.y = (float)sourceRect.y / image->height; uv1.y = uv0.y + (float)(sourceRect.height / image->height); } ImGui::Image((ImTextureID)image, ImVec2(float(destWidth), float(destHeight)),uv0,uv1); }