Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save newpolaris/e9edd948af7eba0d5a87bc05abcba8b9 to your computer and use it in GitHub Desktop.

Select an option

Save newpolaris/e9edd948af7eba0d5a87bc05abcba8b9 to your computer and use it in GitHub Desktop.

Revisions

  1. newpolaris created this gist May 20, 2021.
    555 changes: 555 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,555 @@
    // Dear ImGui: standalone example application for GLFW + OpenGL 3, using programmable pipeline
    // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.)
    // 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

    #define _CRT_SECURE_NO_WARNINGS

    #include "imgui.h"
    #include "imgui_impl_glfw.h"
    #include "imgui_impl_opengl3.h"
    #include <stdio.h>
    #include <glm/glm.hpp>
    #include <vector>
    #include <cstdio>
    #include <cstdarg>
    #include <vector>
    #include <cassert>
    #include <string>

    #include "stb_image.h"

    // About Desktop OpenGL function loaders:
    // Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
    // Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
    // You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
    #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
    #include <GL/gl3w.h> // Initialize with gl3wInit()
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
    #include <GL/glew.h> // Initialize with glewInit()
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
    #include <glad/glad.h> // Initialize with gladLoadGL()
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
    #include <glad/gl.h> // Initialize with gladLoadGL(...) or gladLoaderLoadGL()
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
    #define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
    #include <glbinding/Binding.h> // Initialize with glbinding::Binding::initialize()
    #include <glbinding/gl/gl.h>
    using namespace gl;
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
    #define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
    #include <glbinding/glbinding.h>// Initialize with glbinding::initialize()
    #include <glbinding/gl/gl.h>
    using namespace gl;
    #else
    #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
    #endif

    // Include glfw3.h after our OpenGL definitions
    #include <GLFW/glfw3.h>

    // [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers.
    // To link with VS2010-era libraries, VS2015+ requires linking with legacy_stdio_definitions.lib, which we do using this pragma.
    // Your own project should not be affected, as you are likely to link with a newer binary of GLFW that is adequate for your version of Visual Studio.
    #if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
    #pragma comment(lib, "legacy_stdio_definitions")
    #endif

    static void glfw_error_callback(int error, const char* description)
    {
    fprintf(stderr, "Glfw Error %d: %s\n", error, description);
    }
    #if _WIN32
    extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* _str);
    #else
    # if defined(__OBJC__)
    # import <Foundation/NSObjCRuntime.h>
    # else
    # include <CoreFoundation/CFString.h>
    extern "C" void NSLog(CFStringRef _format, ...);
    # endif
    #endif

    void debug_output(const char* message)
    {
    #if _WIN32
    fprintf(stderr, message);
    OutputDebugStringA(message);
    #else
    # if defined(__OBJC__)
    NSLog(@"%s", message);
    # else
    NSLog(CFSTR("%s"), message);
    # endif
    #endif
    }

    void trace(const char* format...)
    {
    const int kLength = 1024;
    char buffer[kLength + 1] = { 0, };

    va_list argList;
    va_start(argList, format);
    int len = vsnprintf(buffer, kLength, format, argList);
    va_end(argList);
    if (len > kLength)
    len = kLength;
    buffer[len] = '\0';

    debug_output(buffer);
    }

    GLuint createTexture(int32_t width, int32_t height, float* data)
    {
    GLenum format = GL_RGBA;
    GLenum internalFormat = GL_RGBA;

    GLuint instance = 0;
    glGenTextures(1, &instance);
    glActiveTexture(GL_TEXTURE0 + 16 - 1);
    glBindTexture(GL_TEXTURE_2D, instance);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_FLOAT, data);
    glBindTexture(GL_TEXTURE_2D, 0);

    return instance;
    }

    GLuint createTexture(std::string path)
    {
    stbi_set_flip_vertically_on_load(true);

    FILE* fp = fopen(path.c_str(), "rb");
    if (fp == NULL)
    return 0;

    fseek(fp, 0, SEEK_END);
    long length = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    std::vector<char> storage(length);;
    length = (long)fread(storage.data(), 1, length, fp);
    fclose(fp);

    GLenum target = GL_TEXTURE_2D;
    GLenum type = GL_UNSIGNED_BYTE;
    int width = 0, height = 0, nrComponents = 0;
    stbi_uc* imagedata = stbi_load_from_memory(
    (stbi_uc*)storage.data(),
    (int)length, &width, &height, &nrComponents, 0);

    if (!imagedata)
    return 0;

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    GLuint instance = 0;
    glGenTextures(1, &instance);
    glActiveTexture(GL_TEXTURE0 + 16 - 1);
    glBindTexture(target, instance);
    glTexImage2D(target, 0, GL_RGB, width, height, 0, GL_RGB, type, imagedata);
    glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY, 16);
    glGenerateMipmap(target);
    glBindTexture(GL_TEXTURE_2D, 0);

    stbi_image_free(imagedata);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    return instance;
    }

    void bindTexture(GLuint texID, uint8_t slot)
    {
    assert(texID != 0);
    glActiveTexture(GL_TEXTURE0 + slot);
    glBindTexture(GL_TEXTURE_2D, texID);
    };

    GLuint createShader(GLenum type, const char* shaderCode)
    {
    GLuint id = glCreateShader(type);
    if (id == 0)
    return 0;

    glShaderSource(id, 1, &shaderCode, 0);
    glCompileShader(id);

    GLint compiled = 0;
    glGetShaderiv(id, GL_COMPILE_STATUS, &compiled);
    if (compiled == GL_FALSE)
    {
    GLint length = 0;
    glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
    std::vector<GLchar> buffer(length + 1);
    glGetShaderInfoLog(id, length, 0, buffer.data());
    trace("%s (%d) %s\n", __FILE__, __LINE__, buffer.data());
    glDeleteShader(id);
    return 0;
    }
    return id;
    }

    GLuint createProgram(GLuint vertex, GLuint fragment)
    {
    GLuint id = glCreateProgram();

    GLint status = 0;
    if (vertex != 0)
    {
    glAttachShader(id, vertex);
    if (fragment != 0)
    glAttachShader(id, fragment);
    glLinkProgram(id);
    glGetProgramiv(id, GL_LINK_STATUS, &status);

    if (status == GL_FALSE)
    {
    const uint32_t kBufferSize = 512u;
    char log[kBufferSize];
    glGetProgramInfoLog(id, sizeof(log), nullptr, log);
    trace("%s:%d %d: %s", __FILE__, __LINE__, status, log);
    return 0;
    }
    }

    if (status == GL_FALSE)
    {
    glDeleteProgram(id);
    id = 0;
    return id;
    }

    return id;
    }

    struct ProgramDesc
    {
    const char* vertexShader = nullptr;
    const char* fragmentShader = nullptr;
    };

    struct Program
    {
    GLuint vertex = 0;
    GLuint framgment = 0;
    GLuint id = 0;
    };

    GLuint createShader(GLenum type, const char* shaderCode);
    GLuint createProgram(GLuint vertex, GLuint fragment);

    Program createProgram(const ProgramDesc& desc);
    void destroyProgram(Program& program);

    Program createProgram(const ProgramDesc& desc)
    {
    GLuint vs = createShader(GL_VERTEX_SHADER, desc.vertexShader);
    GLuint fs = createShader(GL_FRAGMENT_SHADER, desc.fragmentShader);

    assert(vs != 0);
    assert(fs != 0);

    GLuint id = createProgram(vs, fs);

    Program program;
    program.vertex = vs;
    program.framgment = fs;
    program.id = id;

    return program;
    }

    void destroyProgram(Program& program)
    {
    glDeleteProgram(program.id);
    glDeleteShader(program.vertex);
    glDeleteShader(program.framgment);

    program.vertex = 0;
    program.framgment = 0;
    program.id = 0;
    }


    struct vertex_t {
    glm::vec3 position;
    glm::vec2 texcoord;
    };

    struct VertexArray
    {
    GLuint vao = 0;
    GLuint vbo = 0;
    GLuint ibo = 0;
    size_t vertexCount = 0;
    size_t indexCount = 0;
    };

    VertexArray* createVertexArray(float* vertices, size_t vertexCount, uint32_t* indices, size_t indexCount) {
    GLuint ibo = 0;
    GLuint vbo = 0;
    glGenBuffers(1, &vbo);
    glGenBuffers(1, &ibo);

    const size_t vertexBytes = sizeof(vertex_t)*vertexCount;

    GLuint vao = 0;
    glCreateVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, vertexBytes, vertices, GL_DYNAMIC_DRAW);

    const size_t indexBytes = sizeof(uint32_t) * indexCount;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBytes, indices, GL_DYNAMIC_DRAW);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    GLsizei stride = sizeof(vertex_t);
    auto positionOffset = (const void*)offsetof(vertex_t, position);
    auto texcoordOffset = (const void*)offsetof(vertex_t, texcoord);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, positionOffset);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, texcoordOffset);

    glBindVertexArray(0);

    auto vtxArr = new VertexArray();
    if (vtxArr) {
    vtxArr->vao = vao;
    vtxArr->vbo = vbo;
    vtxArr->ibo = ibo;
    vtxArr->vertexCount = vertexCount;
    vtxArr->indexCount = indexCount;
    }
    return vtxArr;
    }

    int main(int, char**)
    {
    // Setup window
    glfwSetErrorCallback(glfw_error_callback);
    if (!glfwInit())
    return 1;

    // Decide GL+GLSL versions
    #ifdef __APPLE__
    // GL 3.2 + GLSL 150
    const char* glsl_version = "#version 150";
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
    #else
    // GL 3.0 + GLSL 130
    const char* glsl_version = "#version 130";
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
    #endif

    // Create window with graphics context
    GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", NULL, NULL);
    if (window == NULL)
    return 1;
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1); // Enable vsync

    // Initialize OpenGL loader
    #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
    bool err = gl3wInit() != 0;
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
    bool err = glewInit() != GLEW_OK;
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
    bool err = gladLoadGL() == 0;
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
    bool err = gladLoadGL(glfwGetProcAddress) == 0; // glad2 recommend using the windowing library loader instead of the (optionally) bundled one.
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
    bool err = false;
    glbinding::Binding::initialize();
    #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
    bool err = false;
    glbinding::initialize([](const char* name) { return (glbinding::ProcAddress)glfwGetProcAddress(name); });
    #else
    bool err = false; // If you use IMGUI_IMPL_OPENGL_LOADER_CUSTOM, your loader is likely to requires some form of initialization.
    #endif
    if (err)
    {
    fprintf(stderr, "Failed to initialize OpenGL loader!\n");
    return 1;
    }

    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls

    // Setup Dear ImGui style
    ImGui::StyleColorsDark();
    //ImGui::StyleColorsClassic();

    // Setup Platform/Renderer backends
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init(glsl_version);

    // Load Fonts
    // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
    // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
    // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
    // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
    // - Read 'docs/FONTS.md' for more instructions and details.
    // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
    //io.Fonts->AddFontDefault();
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
    //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
    //IM_ASSERT(font != NULL);

    // Our state
    bool show_demo_window = true;
    bool show_another_window = false;
    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

    std::vector<float> vertices = {
    -1.0, -1.0, 0.0, 0.0, 0.0,
    +3.0, -1.0, 0.0, 2.0, 0.0,
    -1.0, +3.0, 0.0, 0.0, 2.0,
    };
    std::vector<uint32_t> indices = {
    0, 1, 2
    };

    auto vertexArray = createVertexArray(
    vertices.data(),
    vertices.size(),
    indices.data(),
    indices.size());

    const char* vertexShader = R"__(
    attribute vec3 a_position;
    attribute vec2 a_texcoord;
    varying vec2 v_texcoord;

    void main() {
    gl_Position = vec4(a_position, 1.0);
    v_texcoord = a_texcoord;
    }
    )__";
    const char* fragmentShader = R"__(
    #ifdef GL_ES
    precision mediump float;
    #endif
    varying vec2 v_texcoord;
    uniform sampler2D u_texture;

    void main() {
    gl_FragColor = texture2D(u_texture, v_texcoord);
    }
    )__";
    auto program = createProgram({vertexShader, fragmentShader});
    glUseProgram(program.id);

    GLuint tmu = 0;
    GLint sampler = glGetUniformLocation(program.id, "u_texture");
    assert(sampler >= 0);
    glUniform1i(sampler, tmu++);

    glUseProgram(0);

    GLuint texture = createTexture("checker.jpg");

    // Main loop
    while (!glfwWindowShouldClose(window))
    {
    // Poll and handle events (inputs, window resize, etc.)
    // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
    // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
    // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
    // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
    glfwPollEvents();

    // Start the Dear ImGui frame
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();

    // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
    if (show_demo_window)
    ImGui::ShowDemoWindow(&show_demo_window);

    // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
    {
    static float f = 0.0f;
    static int counter = 0;

    ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.

    ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
    ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
    ImGui::Checkbox("Another Window", &show_another_window);

    ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
    ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color

    if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
    counter++;
    ImGui::SameLine();
    ImGui::Text("counter = %d", counter);

    ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
    ImGui::End();
    }

    // 3. Show another simple window.
    if (show_another_window)
    {
    ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
    ImGui::Text("Hello from another window!");
    if (ImGui::Button("Close Me"))
    show_another_window = false;
    ImGui::End();
    }

    // Rendering
    ImGui::Render();
    int display_w, display_h;
    glfwGetFramebufferSize(window, &display_w, &display_h);
    glViewport(0, 0, display_w, display_h);
    glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(program.id);
    bindTexture(texture, 0);
    glBindVertexArray(vertexArray->vao);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glUseProgram(0);

    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

    glfwSwapBuffers(window);
    }

    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
    }