Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save newpolaris/e9edd948af7eba0d5a87bc05abcba8b9 to your computer and use it in GitHub Desktop.
Save newpolaris/e9edd948af7eba0d5a87bc05abcba8b9 to your computer and use it in GitHub Desktop.
// 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment