Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gamedev/fggl
  • onuralpsezer/fggl
2 results
Show changes
Showing
with 1262 additions and 1404 deletions
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 30/05/22.
//
#define DEBUG_DRAW_IMPLEMENTATION
#include "fggl/debug/draw.hpp"
\ No newline at end of file
target_link_libraries(fggl PRIVATE imgui)
target_sources(fggl
PRIVATE
imgui_impl_glfw.cpp
imgui_impl_opengl3.cpp
)
target_include_directories(fggl
PRIVATE
include/
)
\ No newline at end of file
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (Requires: GLFW 3.1+)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors.
// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor).
// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter().
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()).
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set.
// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "include/imgui_impl_glfw.h"
// GLFW
#include <GLFW/glfw3.h>
#ifdef _WIN32
#undef APIENTRY
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h> // for glfwGetWin32Window
#endif
#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING
#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED
#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity
#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale
#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface
#ifdef GLFW_RESIZE_NESW_CURSOR // let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
#else
#define GLFW_HAS_NEW_CURSORS (0)
#endif
// Data
enum GlfwClientApi {
GlfwClientApi_Unknown,
GlfwClientApi_OpenGL,
GlfwClientApi_Vulkan
};
static GLFWwindow *g_Window = NULL; // Main window
static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown;
static double g_Time = 0.0;
static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {};
static GLFWcursor *g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
static bool g_InstalledCallbacks = false;
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL;
static GLFWscrollfun g_PrevUserCallbackScroll = NULL;
static GLFWkeyfun g_PrevUserCallbackKey = NULL;
static GLFWcharfun g_PrevUserCallbackChar = NULL;
static const char *ImGui_ImplGlfw_GetClipboardText(void *user_data) {
return glfwGetClipboardString((GLFWwindow *) user_data);
}
static void ImGui_ImplGlfw_SetClipboardText(void *user_data, const char *text) {
glfwSetClipboardString((GLFWwindow *) user_data, text);
}
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
if (g_PrevUserCallbackMousebutton != NULL)
g_PrevUserCallbackMousebutton(window, button, action, mods);
if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed))
g_MouseJustPressed[button] = true;
}
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow *window, double xoffset, double yoffset) {
if (g_PrevUserCallbackScroll != NULL)
g_PrevUserCallbackScroll(window, xoffset, yoffset);
ImGuiIO &io = ImGui::GetIO();
io.MouseWheelH += (float) xoffset;
io.MouseWheel += (float) yoffset;
}
void ImGui_ImplGlfw_KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
ImGuiIO &io = ImGui::GetIO();
if (g_PrevUserCallbackKey != NULL && !io.WantCaptureKeyboard)
g_PrevUserCallbackKey(window, key, scancode, action, mods);
if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)) {
if (action == GLFW_PRESS)
io.KeysDown[key] = true;
if (action == GLFW_RELEASE)
io.KeysDown[key] = false;
}
// Modifiers are not reliable across systems
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
#ifdef _WIN32
io.KeySuper = false;
#else
io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
#endif
}
void ImGui_ImplGlfw_CharCallback(GLFWwindow *window, unsigned int c) {
if (g_PrevUserCallbackChar != NULL)
g_PrevUserCallbackChar(window, c);
ImGuiIO &io = ImGui::GetIO();
io.AddInputCharacter(c);
}
static bool ImGui_ImplGlfw_Init(GLFWwindow *window, bool install_callbacks, GlfwClientApi client_api) {
g_Window = window;
g_Time = 0.0;
// Setup backend capabilities flags
ImGuiIO &io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |=
ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_glfw";
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER;
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
io.ClipboardUserData = g_Window;
#if defined(_WIN32)
io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window);
#endif
// Create mouse cursors
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
// GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting.
// Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.)
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL);
g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
#if GLFW_HAS_NEW_CURSORS
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR);
#else
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
#endif
glfwSetErrorCallback(prev_error_callback);
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
g_PrevUserCallbackMousebutton = NULL;
g_PrevUserCallbackScroll = NULL;
g_PrevUserCallbackKey = NULL;
g_PrevUserCallbackChar = NULL;
if (install_callbacks) {
g_InstalledCallbacks = true;
g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
}
g_ClientApi = client_api;
return true;
}
bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow *window, bool install_callbacks) {
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL);
}
bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow *window, bool install_callbacks) {
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan);
}
bool ImGui_ImplGlfw_InitForOther(GLFWwindow *window, bool install_callbacks) {
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown);
}
void ImGui_ImplGlfw_Shutdown() {
if (g_InstalledCallbacks) {
glfwSetMouseButtonCallback(g_Window, g_PrevUserCallbackMousebutton);
glfwSetScrollCallback(g_Window, g_PrevUserCallbackScroll);
glfwSetKeyCallback(g_Window, g_PrevUserCallbackKey);
glfwSetCharCallback(g_Window, g_PrevUserCallbackChar);
g_InstalledCallbacks = false;
}
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) {
glfwDestroyCursor(g_MouseCursors[cursor_n]);
g_MouseCursors[cursor_n] = NULL;
}
g_ClientApi = GlfwClientApi_Unknown;
}
static void ImGui_ImplGlfw_UpdateMousePosAndButtons() {
// Update buttons
ImGuiIO &io = ImGui::GetIO();
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) {
// 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[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0;
g_MouseJustPressed[i] = false;
}
// Update mouse position
const ImVec2 mouse_pos_backup = io.MousePos;
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
#ifdef __EMSCRIPTEN__
const bool focused = true; // Emscripten
#else
const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0;
#endif
if (focused) {
if (io.WantSetMousePos) {
glfwSetCursorPos(g_Window, (double) mouse_pos_backup.x, (double) mouse_pos_backup.y);
} else {
double mouse_x, mouse_y;
glfwGetCursorPos(g_Window, &mouse_x, &mouse_y);
io.MousePos = ImVec2((float) mouse_x, (float) mouse_y);
}
}
}
static void ImGui_ImplGlfw_UpdateMouseCursor() {
ImGuiIO &io = ImGui::GetIO();
if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|| glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) {
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
} else {
// Show OS mouse cursor
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
glfwSetCursor(g_Window,
g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor]
: g_MouseCursors[ImGuiMouseCursor_Arrow]);
glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
}
static void ImGui_ImplGlfw_UpdateGamepads() {
ImGuiIO &io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Update gamepad inputs
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; }
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; }
int axes_count = 0, buttons_count = 0;
const float *axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);
const unsigned char *buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);
MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f);
MAP_ANALOG(ImGuiNavInput_LStickRight, 0, +0.3f, +0.9f);
MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f);
MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f);
#undef MAP_BUTTON
#undef MAP_ANALOG
if (axes_count > 0 && buttons_count > 0)
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
else
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
}
void ImGui_ImplGlfw_NewFrame() {
ImGuiIO &io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt()
&& "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
glfwGetWindowSize(g_Window, &w, &h);
glfwGetFramebufferSize(g_Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float) w, (float) h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float) display_w / w, (float) display_h / h);
// Setup time step
double current_time = glfwGetTime();
io.DeltaTime = g_Time > 0.0 ? (float) (current_time - g_Time) : (float) (1.0f / 60.0f);
g_Time = current_time;
ImGui_ImplGlfw_UpdateMousePosAndButtons();
ImGui_ImplGlfw_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplGlfw_UpdateGamepads();
}
......@@ -75,17 +75,22 @@
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
// JWR force GLAD to load
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include "include/imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
// GL includes
......@@ -108,6 +113,7 @@
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
#include <glad/gl.h> // Needs to be initialized with gladLoadGL(...) or gladLoaderLoadGL() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
......@@ -145,581 +151,657 @@ using namespace gl;
#endif
// OpenGL Data
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static GLuint g_GlVersion =
0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0,
g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
// Query for GL version (e.g. 320 for GL 3.2)
bool ImGui_ImplOpenGL3_Init(const char *glsl_version) {
// Query for GL version (e.g. 320 for GL 3.2)
#if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major = 0;
GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
if (major == 0 && minor == 0)
{
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
const char* gl_version = (const char*)glGetString(GL_VERSION);
sscanf(gl_version, "%d.%d", &major, &minor);
}
g_GlVersion = (GLuint)(major * 100 + minor * 10);
GLint major = 0;
GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
if (major == 0 && minor == 0) {
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
const char *gl_version = (const char *) glGetString(GL_VERSION);
sscanf(gl_version, "%d.%d", &major, &minor);
}
g_GlVersion = (GLuint) (major * 100 + minor * 10);
#else
g_GlVersion = 200; // GLES 2
g_GlVersion = 200; // GLES 2
#endif
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
// Setup backend capabilities flags
ImGuiIO &io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
if (g_GlVersion >= 320)
io.BackendFlags |=
ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#if defined(IMGUI_IMPL_OPENGL_ES2)
if (glsl_version == NULL)
glsl_version = "#version 100";
if (glsl_version == NULL)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == NULL)
glsl_version = "#version 300 es";
if (glsl_version == NULL)
glsl_version = "#version 300 es";
#elif defined(__APPLE__)
if (glsl_version == NULL)
glsl_version = "#version 150";
if (glsl_version == NULL)
glsl_version = "#version 150";
#else
if (glsl_version == NULL)
glsl_version = "#version 130";
#endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected.
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash below.
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
const char* gl_loader = "Unknown";
IM_UNUSED(gl_loader);
if (glsl_version == NULL)
glsl_version = "#version 130";
#endif
IM_ASSERT((int) strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected.
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash below.
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
const char *gl_loader = "Unknown";
IM_UNUSED(gl_loader);
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
gl_loader = "GL3W";
gl_loader = "GL3W";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
gl_loader = "GLEW";
gl_loader = "GLEW";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
gl_loader = "GLAD";
gl_loader = "GLAD";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
gl_loader = "GLAD2";
gl_loader = "GLAD2";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
gl_loader = "glbinding2";
gl_loader = "glbinding2";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
gl_loader = "glbinding3";
gl_loader = "glbinding3";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
gl_loader = "custom";
gl_loader = "custom";
#else
gl_loader = "none";
gl_loader = "none";
#endif
// Make an arbitrary GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
// Make an arbitrary GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
void ImGui_ImplOpenGL3_Shutdown() {
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
void ImGui_ImplOpenGL3_NewFrame() {
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData *draw_data,
int fb_width,
int fb_height,
GLuint vertex_array_object) {
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (g_GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART);
if (g_GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART);
#endif
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
#if defined(GL_CLIP_ORIGIN)
bool clip_origin_lower_left = true;
if (g_GlVersion >= 450)
{
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);
if (current_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
}
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
bool clip_origin_lower_left = true;
if (g_GlVersion >= 450) {
GLenum current_clip_origin = 0;
glGetIntegerv(GL_CLIP_ORIGIN, (GLint *) &current_clip_origin);
if (current_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
}
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei) fb_width, (GLsizei) fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
#if defined(GL_CLIP_ORIGIN)
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
#endif
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
if (!clip_origin_lower_left) {
float tmp = T;
T = B;
B = tmp;
} // Swap top and bottom if origin is upper left
#endif
const float ortho_projection[4][4] =
{
{2.0f / (R - L), 0.0f, 0.0f, 0.0f},
{0.0f, 2.0f / (T - B), 0.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 0.0f},
{(R + L) / (L - R), (T + B) / (B - T), 0.0f, 1.0f},
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (g_GlVersion >= 330)
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
if (g_GlVersion >= 330)
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
(void) vertex_array_object;
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos,
2,
GL_FLOAT,
GL_FALSE,
sizeof(ImDrawVert),
(GLvoid *) IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV,
2,
GL_FLOAT,
GL_FALSE,
sizeof(ImDrawVert),
(GLvoid *) IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor,
4,
GL_UNSIGNED_BYTE,
GL_TRUE,
sizeof(ImDrawVert),
(GLvoid *) IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
// This is in order to be able to run within an OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData *draw_data) {
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int) (draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int) (draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLuint last_program;
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *) &last_program);
GLuint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
GLuint last_sampler; if (g_GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; }
#endif
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
GLuint last_sampler;
if (g_GlVersion >= 330) {
glGetIntegerv(GL_SAMPLER_BINDING, (GLint *) &last_sampler);
} else {
last_sampler = 0;
}
#endif
GLuint last_array_buffer;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint *) &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
GLuint last_vertex_array_object;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint *) &last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
GLint last_polygon_mode[2];
glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4];
glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4];
glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb;
glGetIntegerv(GL_BLEND_SRC_RGB, (GLint *) &last_blend_src_rgb);
GLenum last_blend_dst_rgb;
glGetIntegerv(GL_BLEND_DST_RGB, (GLint *) &last_blend_dst_rgb);
GLenum last_blend_src_alpha;
glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint *) &last_blend_src_alpha);
GLenum last_blend_dst_alpha;
glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint *) &last_blend_dst_alpha);
GLenum last_blend_equation_rgb;
glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint *) &last_blend_equation_rgb);
GLenum last_blend_equation_alpha;
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint *) &last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
GLboolean last_enable_primitive_restart = (g_GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
GLboolean last_enable_primitive_restart = (g_GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifndef IMGUI_IMPL_OPENGL_ES2
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++) {
const ImDrawList *cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER,
(GLsizeiptr) cmd_list->VtxBuffer.Size * (int) sizeof(ImDrawVert),
(const GLvoid *) cmd_list->VtxBuffer.Data,
GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
(GLsizeiptr) cmd_list->IdxBuffer.Size * (int) sizeof(ImDrawIdx),
(const GLvoid *) cmd_list->IdxBuffer.Data,
GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL) {
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
} else {
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) {
// Apply scissor/clipping rectangle
glScissor((int) clip_rect.x,
(int) (fb_height - clip_rect.w),
(int) (clip_rect.z - clip_rect.x),
(int) (clip_rect.w - clip_rect.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint) (intptr_t) pcmd->GetTexID());
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320)
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
#endif
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
}
// Destroy the temporary VAO
if (g_GlVersion >= 320)
glDrawElementsBaseVertex(GL_TRIANGLES,
(GLsizei) pcmd->ElemCount,
sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT,
(void *) (intptr_t) (pcmd->IdxOffset * sizeof(ImDrawIdx)),
(GLint) pcmd->VtxOffset);
else
#endif
glDrawElements(GL_TRIANGLES,
(GLsizei) pcmd->ElemCount,
sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT,
(void *) (intptr_t) (pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
}
// Destroy the temporary VAO
#ifndef IMGUI_IMPL_OPENGL_ES2
glDeleteVertexArrays(1, &vertex_array_object);
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (g_GlVersion >= 330)
glBindSampler(0, last_sampler);
if (g_GlVersion >= 330)
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
glActiveTexture(last_active_texture);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
if (last_enable_cull_face)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
if (last_enable_depth_test)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
if (last_enable_stencil_test)
glEnable(GL_STENCIL_TEST);
else
glDisable(GL_STENCIL_TEST);
if (last_enable_scissor_test)
glEnable(GL_SCISSOR_TEST);
else
glDisable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (g_GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
if (g_GlVersion >= 310) {
if (last_enable_primitive_restart)
glEnable(GL_PRIMITIVE_RESTART);
else
glDisable(GL_PRIMITIVE_RESTART);
}
#endif
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
glPolygonMode(GL_FRONT_AND_BACK, (GLenum) last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
glViewport(last_viewport[0], last_viewport[1], (GLsizei) last_viewport[2], (GLsizei) last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei) last_scissor_box[2], (GLsizei) last_scissor_box[3]);
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool ImGui_ImplOpenGL3_CreateFontsTexture() {
// Build texture atlas
ImGuiIO &io = ImGui::GetIO();
unsigned char *pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels,
&width,
&height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture);
// Store our identifier
io.Fonts->SetTexID((ImTextureID) (intptr_t) g_FontTexture);
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->SetTexID(0);
g_FontTexture = 0;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture() {
if (g_FontTexture) {
ImGuiIO &io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->SetTexID(0);
g_FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
static bool CheckShader(GLuint handle, const char *desc) {
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean) status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1) {
ImVector<char> buf;
buf.resize((int) (log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar *) buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean) status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
static bool CheckProgram(GLuint handle, const char *desc) {
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean) status == GL_FALSE)
fprintf(stderr,
"ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n",
desc,
g_GlslVersionString);
if (log_length > 1) {
ImVector<char> buf;
buf.resize((int) (log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar *) buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean) status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
bool ImGui_ImplOpenGL3_CreateDeviceObjects() {
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision mediump float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar *vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar *vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar *vertex_shader_glsl_300_es =
"precision mediump float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar *vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar *fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar *fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar *fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar *fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar *vertex_shader = NULL;
const GLchar *fragment_shader = NULL;
if (glsl_version < 130) {
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
} else if (glsl_version >= 410) {
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
} else if (glsl_version == 300) {
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
} else {
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar *vertex_shader_with_version[2] = {g_GlslVersionString, vertex_shader};
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar *fragment_shader_with_version[2] = {g_GlslVersionString, fragment_shader};
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = (GLuint) glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = (GLuint) glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = (GLuint) glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array);
glBindVertexArray(last_vertex_array);
#endif
return true;
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
void ImGui_ImplOpenGL3_DestroyDeviceObjects() {
if (g_VboHandle) {
glDeleteBuffers(1, &g_VboHandle);
g_VboHandle = 0;
}
if (g_ElementsHandle) {
glDeleteBuffers(1, &g_ElementsHandle);
g_ElementsHandle = 0;
}
if (g_ShaderHandle && g_VertHandle) {
glDetachShader(g_ShaderHandle, g_VertHandle);
}
if (g_ShaderHandle && g_FragHandle) {
glDetachShader(g_ShaderHandle, g_FragHandle);
}
if (g_VertHandle) {
glDeleteShader(g_VertHandle);
g_VertHandle = 0;
}
if (g_FragHandle) {
glDeleteShader(g_FragHandle);
g_FragHandle = 0;
}
if (g_ShaderHandle) {
glDeleteProgram(g_ShaderHandle);
g_ShaderHandle = 0;
}
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
......@@ -21,16 +21,16 @@
struct GLFWwindow;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForOpenGL(GLFWwindow *window, bool installCallbacks) -> bool;
IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForVulkan(GLFWwindow *window, bool installCallbacks) -> bool;
IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForOther(GLFWwindow *window, bool installCallbacks) -> bool;
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// GLFW callbacks
// - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks.
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow *window, double xoffset, double yoffset);
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow *window, unsigned int c);
......@@ -25,16 +25,16 @@
#include "imgui.h" // IMGUI_IMPL_API
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
IMGUI_IMPL_API auto ImGui_ImplOpenGL3_Init(const char *glsl_version = nullptr) -> bool;
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData *draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
IMGUI_IMPL_API auto ImGui_ImplOpenGL3_CreateFontsTexture() -> bool;
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API auto ImGui_ImplOpenGL3_CreateDeviceObjects() -> bool;
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Specific OpenGL ES versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
......@@ -66,22 +66,22 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Otherwise try to detect supported Desktop OpenGL loaders..
#elif defined(__has_include)
#if __has_include(<GL/glew.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<glad/gl.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD2
#define IMGUI_IMPL_OPENGL_LOADER_GLAD2
#elif __has_include(<GL/gl3w.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/glbinding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#elif __has_include(<glbinding/Binding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
#else
#error "Cannot detect OpenGL loader!"
#error "Cannot detect OpenGL loader!"
#endif
#else
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
#endif
#endif
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 11/09/22.
//
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
namespace fggl::debug {
auto demangle(const char *name) -> std::string {
int status = -4;
std::unique_ptr<char, decltype(&std::free)> res{
abi::__cxa_demangle(name, nullptr, nullptr, &status),
std::free
};
return (status == 0) ? res.get() : name;
}
}
#else
namespace fggl::debug {
std::string demangle(const char* name) {
return name;
}
}
#endif
#ifndef FGGL_ECS_COMPONENT_H
#define FGGL_ECS_COMPONENT_H
#include "utility.hpp"
#include <cassert>
#include <cstring>
#include <string>
#include <new>
#include <utility>
#include <vector>
#include <unordered_map>
namespace fggl::ecs {
class ComponentBase {
public:
using data_t = unsigned char;
virtual ~ComponentBase() {};
// in place
virtual void destroy(data_t* data) const = 0;
virtual void move(data_t* src, data_t* dest) const = 0;
virtual void bulkMove(data_t* src, data_t* dest, std::size_t count) = 0;
virtual void construct(data_t* data) const = 0;
// virtual
virtual void* construct() const = 0;
virtual void* copyConstruct(const void* src) = 0;
virtual const char* name() const = 0;
virtual const component_type_t id() const = 0;
virtual std::size_t size() const = 0;
};
template<class C>
class Component : public ComponentBase {
public:
virtual void destroy(data_t* data) const override {
C* location = std::launder(reinterpret_cast<C*>(data));
location->~C();
}
virtual const char* name() const override {
return C::name;
}
virtual const component_type_t id() const {
return Component<C>::typeID();
}
virtual void construct(unsigned char* data) const override {
new (data) C();
}
void* copyConstruct(const void* src) override {
const C* srcPtr = (C*)src;
return new C(*srcPtr);
}
void* construct() const override {
return new C();
}
virtual void move(data_t* src, data_t* dest) const override {
assert(src != nullptr);
assert(dest != nullptr);
new (&dest[0]) C(std::move(*reinterpret_cast<C*>(src)));
}
virtual void bulkMove(data_t* src, data_t* dest, std::size_t count) {
if ( std::is_trivially_copyable<C>::value ) {
std::memcpy( dest, src, count * size() );
} else {
unsigned char* srcPtr = src;
unsigned char* destPtr = dest;
for ( std::size_t i = 0; i < count; ++i ){
new (destPtr) C(std::move(*reinterpret_cast<C*>(srcPtr)));
srcPtr += sizeof(C);
destPtr += sizeof(C);
}
}
}
virtual std::size_t size() const {
return sizeof(C);
}
static component_type_t typeID() {
return TypeIdGenerator<ComponentBase>::GetNewID<C>();
}
};
}
#endif
#include "ecs.hpp"
#include <iostream>
using namespace fggl::ecs;
Archetype::Archetype(const archToken_t& token ) : type(token) {
for ( archToken_t::size_type i = 0; i < token.size(); ++i ) {
data.push_back( new unsigned char[default_cap] );
dataSizes.push_back( default_cap );
}
}
ECS::ECS() : m_entityIDCounter(1) {}
ECS::~ECS() {
for( Archetype* arch : m_archetypes ) {
for ( std::size_t i=0; i < arch->type.size(); ++i ) {
const ComponentBase* const comp = m_componentMap[arch->type[i]];
const std::size_t& size = comp->size();
for ( std::size_t e=0; e<arch->entities.size(); ++e ) {
comp->destroy(&arch->data[i][e*size]);
}
delete[] arch->data[i];
}
delete arch;
}
for( componentmap_t::value_type& p : m_componentMap )
delete p.second;
}
entity_t ECS::getNewID() {
return m_entityIDCounter++;
}
entity_t ECS::createEntity( ) {
auto eid = getNewID();
Record dummy;
dummy.archetype = nullptr;
dummy.index = 0;
m_entityArchtypes[ eid ] = dummy;
return eid;
}
Archetype* ECS::getArchetype(const archToken_t& id) {
for ( auto* arch : m_archetypes ) {
if ( arch->type == id ) {
return arch;
}
}
// didn't exist, need to make one
Archetype* arch = new Archetype(id);
m_archetypes.push_back( arch );
return arch;
}
#ifndef FGGL_ECS_H
#define FGGL_ECS_H
#include "utility.hpp"
#include "component.hpp"
#include <iostream>
#include <algorithm>
#include <cassert>
#include <string>
#include <vector>
#include <unordered_map>
namespace fggl::ecs {
using archToken_t = std::vector<component_type_t>;
struct Archetype {
constexpr static unsigned int default_cap = 0;
const archToken_t type;
std::vector<ComponentBase::data_t*> data;
std::vector<std::size_t> dataSizes;
std::vector<entity_t> entities;
Archetype(const archToken_t& type_a);
inline archToken_t create( const component_type_t cid ) const {
assert( !contains(cid) );
// create the new type
auto newType = type;
newType.push_back( cid );
std::sort( newType.begin(), newType.end() );
return newType;
}
inline bool contains( const component_type_t cid ) const {
return ( std::find( type.begin(), type.end(), cid ) != type.end() );
}
};
class ECS {
struct Record {
Archetype* archetype;
std::size_t index;
};
using componentmap_t = std::unordered_map<component_type_t, ComponentBase*>;
using entitymap_t = std::unordered_map<entity_t, Record>;
using archetype_t = std::vector<Archetype*>;
public:
ECS();
~ECS();
entity_t getNewID();
entity_t createEntity();
void removeEntity(const entity_t eid);
template<class C>
void registerComponent() {
component_type_t type = Component<C>::typeID();
if ( m_componentMap.find( type ) != m_componentMap.end() )
return;
m_componentMap.emplace( type, new Component<C> );
}
template<class C>
bool isComponentRegistered() {
component_type_t type = Component<C>::typeID();
return ( m_componentMap.find(type) != m_componentMap.end() );
}
template<class C, typename... Args>
C* addComponent(const entity_t& id, Args&&... args) {
component_type_t type = Component<C>::typeID();
assert( isComponentRegistered<C>() );
Record& record = m_entityArchtypes[id];
Archetype* oldArch = record.archetype;
C* newComp = nullptr;
Archetype* newArch = nullptr;
if ( !oldArch ) {
archToken_t newID(1, type);
const ComponentBase* const newCompType = m_componentMap[ type ];
// fetch type
newArch = getArchetype( newID );
assert( newArch->type.size() == 1 );
// calculate if we have enouph space to allocate
std::size_t emplacementPos = ensureCapacity( newArch, 0, newCompType );
newComp = new (&newArch->data[0][emplacementPos])C(std::forward<Args>(args)...);
} else {
// check if the arch contains the component
if ( oldArch->contains(type) ) {
return nullptr;
}
// create a new archetype with the component
auto newID = oldArch->create( type );
newArch = getArchetype( newID );
// relocate the old data to the new archetype
for ( std::size_t j=0; j < newID.size(); ++j ){
const component_type_t compType = newID[j];
const ComponentBase* const comp = m_componentMap.at(compType);
// TODO this seems a little suspect - surely we could allocate all blocks at once?
// if per component the arrays could become out of sync...
int newOffset = ensureCapacity(newArch, j, comp);
int oldIdx = getComponentIdx(oldArch, compType);
if ( oldIdx != -1 ) {
assert( oldArch->contains(compType) );
const std::size_t compSize = comp->size();
const std::size_t oldOffset = record.index * compSize;
comp->move( &oldArch->data[oldIdx][oldOffset],
&newArch->data[j][newOffset] );
comp->destroy( &oldArch->data[oldIdx][oldOffset] );
} else {
assert( !oldArch->contains(compType) );
newComp = new (&newArch->data[j][newOffset])
C(std::forward<Args>(args)...);
}
}
// ensure the old archetype is still contigious
const int lastEnt = oldArch->entities.size() - 1;
if ( lastEnt != record.index ) {
for ( std::size_t i=0; i < oldArch->type.size(); ++i ) {
const component_type_t typeID = oldArch->type[i];
const ComponentBase* const comp = m_componentMap[typeID];
const std::size_t& compSize = comp->size();
// shift the empty record to the end of the list
std::size_t slotOffset = record.index * compSize;
std::size_t lastOffset = lastEnt * compSize;
// if we're not the last entity, swap
if ( slotOffset != lastOffset ) {
comp->move( &oldArch->data[i][lastOffset],
&oldArch->data[i][slotOffset] );
comp->destroy( &oldArch->data[i][lastOffset] );
}
}
// fix the position
oldArch->entities[ record.index ] = oldArch->entities[ lastEnt ];
}
oldArch->entities.pop_back();
}
// register the new data with the new archetype
newArch->entities.push_back( id );
record.index = newArch->entities.size() - 1;
record.archetype = newArch;
return newComp;
}
template<class C>
void removeComponent(const entity_t& entityId);
template<class C>
bool hasComponent(const entity_t& entityId) const {
const component_type_t componentID = Component<C>::typeID();
const auto* arch = m_entityArchtypes.at(entityId).archetype;
if ( arch == nullptr ) {
return false;
}
return ( std::find( arch->type.begin(), arch->type.end(), componentID) !=
arch->type.end() );
}
template<class C>
C* getComponent(const entity_t& entityId) {
assert( hasComponent<C>( entityId ) );
const auto type = Component<C>::typeID();
const ComponentBase* const newComp = m_componentMap[type];
const auto record = m_entityArchtypes.at(entityId);
const auto* arch = record.archetype;
// JWR: linear search... seems a little suspect, they're ordered after all
for ( std::size_t i=0; i < arch->type.size(); ++i ) {
if ( arch->type[i] == type ) {
return reinterpret_cast<C*>(& (arch->data[i][ record.index * newComp->size() ]) );
}
}
return nullptr;
}
template<class C>
const C* getComponent(const entity_t& entityId) const {
assert( hasComponent<C>( entityId ) );
const auto type = Component<C>::typeID();
const ComponentBase* const newComp = m_componentMap.at(type);
const auto record = m_entityArchtypes.at(entityId);
const auto* arch = record.archetype;
// JWR: linear search... seems a little suspect, they're ordered after all
for ( std::size_t i=0; i < arch->type.size(); ++i ) {
if ( arch->type[i] == type ) {
return reinterpret_cast<C*>(& (arch->data[i][ record.index * newComp->size() ]) );
}
}
return nullptr;
}
template<class... Cs>
std::vector<entity_t> getEntityWith() const {
// construct the key
archToken_t key;
(key.push_back( Component<Cs>::typeID() ), ...);
// entities
std::vector<entity_t> entities;
for ( Archetype* arch : m_archetypes ) {
if ( std::includes( arch->type.begin(), arch->type.end(), key.begin(), key.end() ) ) {
if( !arch->entities.empty() ) {
entities.insert( entities.begin(), arch->entities.begin(), arch->entities.end() );
}
}
}
return entities;
}
private:
entitymap_t m_entityArchtypes;
archetype_t m_archetypes;
entity_t m_entityIDCounter;
componentmap_t m_componentMap;
Archetype* getArchetype(const archToken_t& id);
inline std::string arch2str(Archetype* arch) {
std::string str;
for ( const auto& type : arch->type ) {
str += std::to_string( type );
}
return str;
}
inline std::size_t ensureCapacity(Archetype* arch, const int idx, const ComponentBase* const comp) {
const std::size_t& compSize = comp->size();
std::size_t currSize = arch->entities.size() * compSize;
std::size_t newSize = currSize + compSize;
std::size_t cap = arch->dataSizes[idx];
if ( newSize > arch->dataSizes[idx] ) {
arch->dataSizes[idx] *= 2;
arch->dataSizes[idx] += compSize;
// copy data over
unsigned char* newData = new unsigned char[arch->dataSizes[idx]];
for ( std::size_t e=0; e<arch->entities.size(); ++e ) {
const int offset = e * compSize;
comp->move( &arch->data[idx][offset], &newData[offset] );
comp->destroy( &arch->data[idx][offset] );
}
// free the old data and swap the pointers
delete[] arch->data[idx];
arch->data[idx] = newData;
}
return currSize;
}
inline int getComponentIdx(const Archetype* arch, const component_type_t goal) {
// JWR could do binary search for speedup
for ( std::size_t i=0; i < arch->type.size(); ++i ) {
if ( arch->type[i] == goal ) return i;
}
return -1;
}
};
}
#endif
#ifndef FGGL_ECS_UTILITY_H
#define FGGL_ECS_UTILITY_H
#include <cstdint>
namespace fggl::ecs {
using IDType = std::uint32_t;
using entity_t = IDType;
using component_type_t = IDType;
constexpr IDType NULL_ENTITY = 0;
template<class T>
class TypeIdGenerator {
private:
static IDType m_count;
public:
template<class U>
static const IDType GetNewID() {
static const IDType idCounter = m_count++;
return idCounter;
}
};
template<class T> IDType TypeIdGenerator<T>::m_count = 0;
}
#endif
#ifndef FGGL_ECS3_ECS_H
#define FGGL_ECS3_ECS_H
#include <fggl/ecs3/module/module.h>
#include <fggl/ecs3/prototype/world.h>
namespace fggl::ecs3 {
using World = prototype::World;
}
#endif
\ No newline at end of file
//
// Created by webpigeon on 23/10/2021.
//
#include "Container.h"
namespace fggl::ecs3 {
std::ostream& operator<<(std::ostream& out, RecordIdentifier const& curr) {
out << "record(";
for (int i=0; i<curr.count; i++) {
out << i;
if ( i != curr.count-1) {
out << ",";
}
}
out << ")";
return out;
}
std::size_t Container::create() {
ensure(m_size + 1);
auto pos = m_size++;
// setup entry
for (std::size_t i = 0; i < m_identifier.count; ++i) {
auto compMeta = m_types.meta(m_identifier.types[i]);
auto offset = offsets[i] + (pos * compMeta->size());
auto compPtr = backingStore + offset;
compMeta->construct(compPtr);
}
return pos;
}
void Container::remove(std::size_t pos) {
// cleanup entry
for (std::size_t i = 0; i < m_identifier.count; ++i) {
auto compMeta = m_types.meta(m_identifier.types[i]);
auto offset = offsets[i] + (pos * compMeta->size());
auto compPtr = backingStore + offset;
compMeta->destroy(compPtr);
}
// if we're not the last one, swap places
if (pos != m_size - 1) {
move(pos, *this, m_size - 1);
}
m_size--;
}
void Container::move(std::size_t newPos, Container &oldContainer, std::size_t oldPos) {
for (std::size_t i = 0; i < m_identifier.count; ++i) {
auto thisComp = m_identifier.types[i];
auto compMeta = m_types.meta(thisComp);
auto newPtr = backingStore + (offsets[i] + (newPos * compMeta->size()));
auto oldPtr = oldContainer.data_raw(thisComp) + (oldPos * compMeta->size());
compMeta->move(oldPtr, newPtr);
compMeta->destroy(oldPtr);
}
}
std::size_t Container::expand(Container &other, std::size_t otherPos, component_type_t newComp) {
ensure(m_size + 1);
auto pos = m_size++;
for (std::size_t i = 0; i < m_identifier.count; ++i) {
auto thisComp = m_identifier.types[i];
auto compMeta = m_types.meta(thisComp);
auto newPtr = backingStore + (offsets[i] + (pos * compMeta->size()));
if (newComp != thisComp) {
// we're moving the component
auto oldPtr = other.data_raw(thisComp) + (otherPos * compMeta->size());
compMeta->move(oldPtr, newPtr);
} else {
// this is the new comp
compMeta->construct(newPtr);
}
}
// remove the old entity
other.remove(otherPos);
return pos;
}
void Container::contract(Container &other, std::size_t otherPos) {
ensure(m_size + 1);
auto pos = m_size++;
move(pos, other, otherPos);
for (std::size_t i = 0; i < m_identifier.count; ++i) {
auto thisComp = m_identifier.types[i];
auto compMeta = m_types.meta(thisComp);
auto newPtr = backingStore + (offsets[i] + (pos * compMeta->size()));
auto oldPtr = other.data_raw(thisComp) + (otherPos * compMeta->size());
compMeta->move(oldPtr, newPtr);
}
other.remove(otherPos);
}
void Container::ensure(std::size_t size) {
if (size < m_capacity) {
return;
}
std::size_t required = 0;
for (std::size_t i = 0; i < m_identifier.count; i++) {
auto meta = m_types.meta(m_identifier.types[i]);
required += meta->size() * size;
}
auto *newBacking = new unsigned char[required];
std::size_t newOffsets[RecordIdentifier::MAX_COMPS];
std::size_t currOffset = 0;
// bulk copy routine
for (std::size_t cid = 0; cid < m_identifier.count; ++cid) {
auto compMeta = m_types.meta(m_identifier.types[cid]);
newOffsets[cid] = currOffset;
compMeta->bulkMove(
backingStore + offsets[cid],
newBacking + newOffsets[cid],
m_size);
currOffset += size * compMeta->size();
}
// swap the backing store and cleanup the old one
auto *tmp = backingStore;
backingStore = newBacking;
delete[] tmp;
// update size info
std::memcpy(offsets, newOffsets, m_identifier.count * sizeof(std::size_t));
m_capacity = size;
}
}
\ No newline at end of file
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_CONTAINER_H
#define FGGL_ECS3_CONTAINER_H
#include <cstdint>
#include <cstdarg>
#include <cassert>
#include <fggl/ecs3/types.hpp>
namespace fggl::ecs3 {
class Container {
public:
const RecordIdentifier m_identifier;
Container(const TypeRegistry& reg, RecordIdentifier id) :
m_types(reg),
m_identifier( id ),
backingStore(nullptr),
m_capacity(0),
m_size(0) {
};
~Container() {
delete[] backingStore;
}
std::size_t create();
void remove(std::size_t pos);
std::size_t expand(Container& other, std::size_t otherPos, component_type_t newComp);
void contract(Container& other, std::size_t otherPos);
void ensure(std::size_t size);
inline unsigned char* data_raw(component_type_t type) {
auto seek_id = m_identifier.idx(type);
// you asked for something I don't contain...
if ( seek_id == m_identifier.count ) {
std::cerr << "asked for " << type << " from " << m_identifier << std::endl;
assert( seek_id != m_identifier.count );
return nullptr;
}
// figure out the offset
return backingStore + offsets[seek_id];
}
template<typename T>
inline T* data() {
auto comp_id = Component<T>::typeID();
return (T*)data_raw(comp_id);
}
template<typename T>
T* set(std::size_t entity, T* compData) {
auto* comps = data<T>();
auto entityPos = idx(entity);
auto compMeta = m_types.template meta<T>();
unsigned char* usrPtr = (unsigned char*)&comps[entityPos];
compMeta->destroy(usrPtr);
compMeta->move((unsigned char*)compData, usrPtr);
return &comps[entityPos];
}
[[nodiscard]]
inline std::size_t size() const {
return m_size;
}
inline std::size_t idx(entity_t entity) {
auto* entityData = data<EntityMeta>();
for (int i=0; i<m_size; i++) {
if ( entityData[i].id == entity ) {
return i;
}
}
return m_size;
}
private:
const TypeRegistry& m_types;
unsigned char* backingStore;
std::size_t offsets[RecordIdentifier::MAX_COMPS]{};
std::size_t m_size;
std::size_t m_capacity;
void move(std::size_t newPos, Container& oldContainer, std::size_t oldPos);
};
}
#endif //FGGL_ECS3_CONTAINER_H
#ifndef FGGL_ECS_ECS3_H
#define FGGL_ECS_ECS3_H
#include <cstddef>
#include <cstdarg>
#include <cassert>
#include <cstring>
#include <map>
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <fggl/ecs3/utils.hpp>
#include <fggl/ecs3/types.hpp>
#include <fggl/ecs3/fast/Container.h>
namespace fggl::ecs3::fast {
using entity_t = unsigned int;
constexpr entity_t NULL_ENTITY = 0;
class World {
public:
explicit World(TypeRegistry& reg) : m_registry(reg), m_last(NULL_ENTITY) {}
entity_t create() {
auto next = m_last++;
auto arch = make_id(1, Component<EntityMeta>::typeID());
auto& container = getContainer(arch);
m_entities[next] = container.m_identifier;
auto pos = container.create();
auto* entityMeta = container.data<EntityMeta>();
entityMeta[pos].id = next;
return next;
}
void remove(entity_t entity) {
auto arch = m_entities.at(entity);
auto container = m_records.at(arch);
auto entPos = container.idx(entity);
container.remove(entPos);
}
inline Container& getContainer(RecordIdentifier& arch) {
try {
return m_records.at(arch);
} catch (std::out_of_range& e) {
auto v = m_records.emplace(std::pair<RecordIdentifier, Container>(arch, {m_registry, arch}));
return v.first->second;
}
}
template<typename T>
T* add(const entity_t entity){
auto currArch = m_entities.at( entity );
auto newArch = currArch.with<T>();
m_entities[entity] = newArch;
auto& oldContainer = m_records.at(currArch);
auto& newContainer = getContainer(newArch);
auto oldPos = oldContainer.idx(entity);
auto newPos = newContainer.expand(oldContainer, oldPos, Component<T>::typeID());
auto* data = newContainer.template data<T>();
return &data[newPos];
}
template<typename T>
T* set(const entity_t entity, T* record) {
auto currArch = m_entities.at( entity );
// check we already have that component type...
if ( currArch.idx(Component<T>::typeID()) == currArch.count ) {
add<T>(entity);
currArch = m_entities.at( entity );
}
auto& container = m_records.at(currArch);
auto pos = container.idx(entity);
return container.set<T>(pos, record);
}
template<typename T>
T* get(entity_t entity) {
auto currArch = m_entities.at( entity );
auto pos = currArch.idx(entity);
auto& container = m_records.at(currArch);
auto* data = container.template data<T>();
return &data[pos];
}
template<typename T>
const T* get(entity_t entity) const {
auto currArch = m_entities.at( entity );
auto pos = currArch.idx(entity);
auto& container = m_records.at(currArch);
auto* data = container.template data<T>();
return &data[pos];
}
template<typename T>
void remove(entity_t entity) {
auto currArch = m_entities.at( entity );
auto newArch = currArch.without<T>();
auto& oldContainer = m_records[currArch];
auto& newContainer = m_records[newArch];
auto oldPos = oldContainer.idx(entity);
auto newPos = newContainer.create();
m_records[newArch].contract(newPos, oldContainer, oldPos);
}
template<typename... T>
std::vector<entity_t> findMatching() const {
return {};
}
private:
TypeRegistry& m_registry;
std::map<RecordIdentifier, Container> m_records;
std::map<entity_t, RecordIdentifier> m_entities;
entity_t m_last{};
};
}
#endif
//
// Created by webpigeon on 23/10/2021.
//
#include "module.h"
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_MODULE_H
#define FGGL_ECS3_MODULE_H
#include <string>
#include <map>
#include <memory>
#include <fggl/ecs3/types.hpp>
namespace fggl::ecs3 {
class Module {
public:
virtual ~Module() = default;
[[nodiscard]] virtual std::string name() const = 0;
virtual void onLoad(ModuleManager& manager, TypeRegistry& tr) {};
};
class ModuleManager {
public:
explicit ModuleManager(TypeRegistry& types) : m_types(types), m_modules(){ }
~ModuleManager() = default;
template<typename C, typename... Args>
std::shared_ptr<C> load(Args&... args) {
auto ptr = std::make_shared<C>(args...);
m_modules[ptr->name()] = ptr;
ptr->onLoad(*this, m_types);
std::cerr << "module loaded: " << ptr->name() << std::endl;
return ptr;
}
template<typename C>
void onAdd(const callback_t& cb) {
m_types.callbackAdd( Component<C>::typeID(), cb);
}
private:
TypeRegistry& m_types;
std::map<std::string, std::shared_ptr<Module>> m_modules;
};
}
#endif //FGGL_ECS3_MODULE_H
//
// Created by webpigeon on 23/10/2021.
//
#include "world.h"
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_PROTOTYPE_WORLD_H
#define FGGL_ECS3_PROTOTYPE_WORLD_H
#include <map>
#include <fggl/ecs3/types.hpp>
/**
* A component based implementation of a game world.
*
* This is not a true ECS but exposes a similar API to it for testing (with a lot less headaches).
*/
namespace fggl::ecs3::prototype {
class Entity {
public:
bool m_abstract;
explicit Entity(entity_t id) : m_id(id), m_abstract(false) {};
Entity(const Entity& entity) : m_id(entity.m_id), m_components(entity.m_components) {
std::cerr << "someone be doing one of them copy things: " << m_id << std::endl;
}
~Entity() = default;
template<typename C>
C* add() {
C* ptr = new C();
m_components[Component<C>::typeID()] = ptr;
return ptr;
}
void* add(std::shared_ptr<ComponentBase> t) {
void* ptr = t->construct();
m_components[t->id()] = ptr;
return ptr;
}
template<typename C>
C* set(const C* ptr) {
C* newPtr = new C(*ptr);
m_components[Component<C>::typeID()] = newPtr;
return newPtr;
}
void* set(const std::shared_ptr<ComponentBase>& t, const void* ptr) {
void* newPtr = t->copyConstruct(ptr);
m_components[t->id()] = newPtr;
return newPtr;
}
template<typename C>
C* get() {
void* ptr = m_components.at(Component<C>::typeID());
return (C*)ptr;
}
inline void* get(component_type_t t){
return m_components.at(t);
}
std::vector<component_type_t> getComponentIDs() {
std::vector<component_type_t> comps{};
for (auto& [k,_] : m_components) {
comps.push_back(k);
}
return comps;
}
bool hasComponents(std::vector<component_type_t>& Cs) {
for (auto c : Cs) {
if ( m_components.find(c) == m_components.end() ) {
return false;
}
}
return true;
}
private:
entity_t m_id;
std::map<component_type_t, void*> m_components;
};
class World {
public:
explicit World(TypeRegistry& reg) : m_types(reg), m_next(1), m_entities() {};
~World() = default;
entity_t create(bool abstract) {
auto nextID = m_next++;
m_entities.emplace(nextID, nextID);
auto& entity = m_entities.at(nextID);
entity.m_abstract = abstract;
// meta data
auto* meta = entity.add<ecs3::EntityMeta>();
meta->id = nextID;
meta->abstract = abstract;
return nextID;
}
entity_t copy(entity_t prototype) {
auto clone = create(false);
auto components = getComponents(prototype);
for (auto component : components) {
auto protoComp = get(prototype, component);
set(clone, component, protoComp);
}
return clone;
}
void destroy(entity_t entity) {
// TOOD resolve and clean components
m_entities.erase(entity);
}
inline TypeRegistry& types() {
return m_types;
}
std::vector<entity_t> all() {
std::vector<entity_t> entities{};
for( auto& [eid, entity] : m_entities) {
entities.push_back(eid);
}
return entities;
}
std::vector<component_type_t> getComponents(entity_t entityID){
std::vector<component_type_t> components{};
auto& entity = m_entities.at(entityID);
auto comps = entity.getComponentIDs();
for (auto id : comps) {
components.push_back(id);
}
return components;
}
template<typename... Cs>
std::vector<entity_t> findMatching() {
// construct the key
std::vector<ecs::component_type_t> key;
(key.push_back( Component<Cs>::typeID() ), ...);
// entities
std::vector<entity_t> entities{};
for( auto& [eid, entity] : m_entities) {
if ( entity.hasComponents(key) && !entity.m_abstract ) {
entities.push_back(eid);
}
}
return entities;
}
template<typename C>
C* add(entity_t entity_id) {
std::cerr << "[>>] adding comp " << C::name << " to " << entity_id << std::endl;
auto& entity = m_entities.at(entity_id);
auto comp = entity.template add<C>();
m_types.fireAdd(this, entity_id, Component<C>::typeID());
return comp;
}
void* add(entity_t entity_id, component_type_t component_id) {
auto meta = m_types.meta(component_id);
auto& entity = m_entities.at(entity_id);
void* ptr = entity.add(meta);
m_types.fireAdd(this, entity_id, meta->id());
return ptr;
}
template<typename C>
C* set(entity_t entity_id, const C* ptr) {
std::cerr << "[>>] setting comp " << C::name << " to " << entity_id << std::endl;
auto& entity = m_entities.at(entity_id);
auto comp = entity.set<C>(ptr);
m_types.fireAdd(this, entity_id, Component<C>::typeID());
return comp;
}
void* set(entity_t entity_id, component_type_t cid, const void* ptr){
auto& entity = m_entities.at(entity_id);
auto cMeta = m_types.meta(cid);
auto comp = entity.set(cMeta, ptr);
m_types.fireAdd(this, entity_id, cid);
return comp;
}
template<typename C>
C* get(entity_t entity_id) {
auto& entity = m_entities.at(entity_id);
return entity.get<C>();
}
void* get(entity_t entity_id, component_type_t t){
auto& entity = m_entities.at(entity_id);
return entity.get(t);
}
private:
TypeRegistry& m_types;
entity_t m_next;
std::map<entity_t, Entity> m_entities;
};
}
#endif //FGGL_ECS3_PROTOTYPE_WORLD_H
\ No newline at end of file
#ifndef FGGL_ECS3_TYPES_H
#define FGGL_ECS3_TYPES_H
#include <cstdarg>
#include <utility>
#include <fggl/ecs/component.hpp>
#include <fggl/ecs3/utils.hpp>
#include <iostream>
#include <memory>
#include <algorithm>
#include <map>
#include <unordered_map>
namespace fggl::ecs3 {
namespace {
using namespace fggl::ecs;
};
namespace prototype {
class World;
}
using fggl::ecs::component_type_t;
class ModuleManager;
using callback_t = std::function<void(prototype::World*, ecs3::entity_t)>;
struct TypeCallbacks {
std::vector<callback_t> add;
};
// core component types
struct EntityMeta {
constexpr static const char name[] = "meta";
entity_t id;
bool abstract;
};
struct RecordIdentifier {
constexpr static std::size_t MAX_COMPS = 32;
component_type_t types[MAX_COMPS];
std::size_t count;
[[nodiscard]]
inline std::size_t idx(component_type_t t) const {
return utils::search(types, count, t);
}
template<typename T>
[[nodiscard]]
RecordIdentifier with() const {
// check the caller wasn't a muppet
const auto typeID = ecs::Component<T>::typeID();
if ( idx(typeID) != count ) {
return *this;
}
RecordIdentifier re{};
re.count = count + 1;
re.types[ count ] = ecs::Component<T>::typeID();
// add old types
for (std::size_t i = 0; i < count; ++i) {
re.types[i] = types[i];
}
std::sort( re.types, re.types + re.count );
return re;
}
template<typename T>
[[nodiscard]]
RecordIdentifier without() const {
// check the caller wasn't a muppet
const auto typeID = ecs::Component<T>::typeID();
const auto typeIdx = idx(typeID);
if ( typeIdx == count ) {
return *this;
}
RecordIdentifier re{};
re.count = count - 1;
// add old types
for (std::size_t i = 0, j = 0; i < count; ++i) {
if (typeIdx != i) {
re.types[j] = types[i];
j++;
}
}
std::sort( re.types, re.types + re.count );
return re;
}
bool operator<(const RecordIdentifier& other) const {
if ( count < other.count) {
return true;
} else if ( count > other.count ) {
return false;
} else {
for (int i = 0; i < count; i++) {
if ( types[i] != other.types[i] ) {
return types[i] < other.types[i];
}
}
return false;
}
}
bool operator==(const RecordIdentifier& arg) const {
if ( arg.count != count ) {
return false;
}
for (int i=0; i<count; i++) {
if ( types[i] != arg.types[i] ) {
return false;
}
}
return true;
}
bool operator!=(const RecordIdentifier& arg) const {
return !( *this == arg );
}
};
std::ostream& operator<<(std::ostream& out, RecordIdentifier const& curr);
inline RecordIdentifier make_id(std::size_t count, ...) {
assert(count < RecordIdentifier::MAX_COMPS);
RecordIdentifier re{};
std::va_list args;
va_start(args, count);
for ( std::size_t i = 0; i < count; ++i ) {
re.types[i] = va_arg(args, component_type_t);
}
va_end(args);
re.count = count;
std::sort( re.types, re.types + count );
return re;
}
class TypeRegistry {
public:
TypeRegistry() : m_last_virtual(9000), m_callbacks() {
// core types always exist
make<EntityMeta>();
}
template<typename T>
void make() {
auto type_id = Component<T>::typeID();
if ( m_types.find( type_id ) != m_types.end() )
return;
m_types[type_id] = std::make_shared<Component<T>>();
}
template<typename T>
bool exists() {
auto type_id = Component<T>::typeID();
return m_types.find( type_id ) != m_types.end();
}
template<typename T>
std::shared_ptr<fggl::ecs::ComponentBase> meta() const {
auto type_id = Component<T>::typeID();
return m_types.at( type_id );
}
inline std::shared_ptr<fggl::ecs::ComponentBase> meta(component_type_t type_id) const {
return m_types.at( type_id );
}
inline component_type_t find(const char* name) const {
for (auto& [type, meta] : m_types) {
if ( std::strcmp(name, meta->name()) == 0) {
return type;
}
}
return 0;
}
inline void make_virtual(std::shared_ptr<ComponentBase> vtype) {
auto type_id = m_last_virtual++;
m_types[type_id] = std::move(vtype);
}
void callbackAdd(component_type_t component, const callback_t& callback) {
m_callbacks[component].add.push_back(callback);
}
void fireAdd(prototype::World* world, entity_t entity, component_type_t type) {
try {
auto& callbacks = m_callbacks.at(type).add;
for ( auto& callback : callbacks) {
callback(world, entity);
}
} catch ( std::out_of_range& e) {
std::cerr << "no callbacks for: " << m_types[type]->name() << std::endl;
}
}
private:
std::unordered_map<component_type_t, std::shared_ptr<fggl::ecs::ComponentBase>> m_types;
component_type_t m_last_virtual;
std::map<component_type_t, TypeCallbacks> m_callbacks;
};
};
#endif