From cb3d50394151b6e5fefcf4d0269940144dc675bb Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 13 Aug 2014 11:43:51 +0100 Subject: [PATCH] OpenGL example now use the fixed function-pipeline. Code down by 120 lines. --- examples/opengl_example/main.cpp | 209 ++++++------------------------- 1 file changed, 39 insertions(+), 170 deletions(-) diff --git a/examples/opengl_example/main.cpp b/examples/opengl_example/main.cpp index cb5b8c4e1..44d77f5ef 100644 --- a/examples/opengl_example/main.cpp +++ b/examples/opengl_example/main.cpp @@ -2,87 +2,63 @@ #include #include #define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" // for .png loading +#include "stb_image.h" // for .png loading #include "../../imgui.h" #ifdef _MSC_VER -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif static GLFWwindow* window; -static GLuint vbo; -static GLuint vao; -static GLuint vertexShader; -static GLuint fragmentShader; -static GLuint shaderProgram; static GLuint fontTex; // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structuer) +// We are using the fixed pipeline. +// A faster way would be to collate all vertices from all cmd_lists into a single vertex buffer static void ImImpl_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) { - size_t total_vtx_count = 0; - for (int n = 0; n < cmd_lists_count; n++) - total_vtx_count += cmd_lists[n]->vtx_buffer.size(); - if (total_vtx_count == 0) + if (cmd_lists_count == 0) return; - // Copy all vertices into a single contiguous GL buffer - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBindVertexArray(vao); - glBufferData(GL_ARRAY_BUFFER, total_vtx_count * sizeof(ImDrawVert), NULL, GL_STREAM_DRAW); - unsigned char* buffer_data = (unsigned char*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - if (!buffer_data) - return; - for (int n = 0; n < cmd_lists_count; n++) - { - const ImDrawList* cmd_list = cmd_lists[n]; - memcpy(buffer_data, &cmd_list->vtx_buffer[0], cmd_list->vtx_buffer.size() * sizeof(ImDrawVert)); - buffer_data += cmd_list->vtx_buffer.size() * sizeof(ImDrawVert); - } - glUnmapBuffer(GL_ARRAY_BUFFER); - // Setup render state: alpha-blending enabled, no face culling, no depth testing glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); + //glEnable(GL_SCISSOR_TEST); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); - // Bind texture and enable our shader + // Bind texture glBindTexture(GL_TEXTURE_2D, fontTex); - glUseProgram(shaderProgram); - const GLint uniMVP = glGetUniformLocation(shaderProgram, "MVP"); - const GLint uniClipRect = glGetUniformLocation(shaderProgram, "ClipRect"); + glEnable(GL_TEXTURE_2D); - // Setup orthographic projection matrix - const float width = ImGui::GetIO().DisplaySize.x; - const float height = ImGui::GetIO().DisplaySize.y; - const float mvp[4][4] = - { - { 2.0f/width, 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/-height, 0.0f, 0.0f }, - { 0.0f, 0.0f, -1.0f, 0.0f }, - { -1.0f, 1.0f, 0.0f, 1.0f }, - }; - glUniformMatrix4fv(uniMVP, 1, GL_FALSE, &mvp[0][0]); + // Setup matrices + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y, 0.0f, -1.0f, +1.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); // Render command lists - int vtx_offset = 0; for (int n = 0; n < cmd_lists_count; n++) { const ImDrawList* cmd_list = cmd_lists[n]; + const unsigned char* vtx_buffer = (const unsigned char*)cmd_list->vtx_buffer.begin(); + glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer)); + glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer+8)); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (void*)(vtx_buffer+16)); + + int vtx_offset = 0; const ImDrawCmd* pcmd_end = cmd_list->commands.end(); for (const ImDrawCmd* pcmd = cmd_list->commands.begin(); pcmd != pcmd_end; pcmd++) { - glUniform4fv(uniClipRect, 1, (float*)&pcmd->clip_rect); + glScissor((int)pcmd->clip_rect.x, (int)pcmd->clip_rect.y, (int)(pcmd->clip_rect.z - pcmd->clip_rect.x), (int)(pcmd->clip_rect.w - pcmd->clip_rect.y)); glDrawArrays(GL_TRIANGLES, vtx_offset, pcmd->vtx_count); vtx_offset += pcmd->vtx_count; } } - - // Cleanup GL state - glBindTexture(GL_TEXTURE_2D, 0); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glUseProgram(0); + glDisable(GL_SCISSOR_TEST); } static const char* ImImpl_GetClipboardTextFn() @@ -103,42 +79,10 @@ static void ImImpl_SetClipboardTextFn(const char* text, const char* text_end) free(buf); } -// Shader sources -const GLchar* vertexSource = - "#version 150 core\n" - "uniform mat4 MVP;" - "in vec2 i_pos;" - "in vec2 i_uv;" - "in vec4 i_col;" - "out vec4 col;" - "out vec2 pixel_pos;" - "out vec2 uv;" - "void main() {" - " col = i_col;" - " pixel_pos = i_pos;" - " uv = i_uv;" - " gl_Position = MVP * vec4(i_pos.x, i_pos.y, 0.0f, 1.0f);" - "}"; - -const GLchar* fragmentSource = - "#version 150 core\n" - "uniform sampler2D Tex;" - "uniform vec4 ClipRect;" - "in vec4 col;" - "in vec2 pixel_pos;" - "in vec2 uv;" - "out vec4 o_col;" - "void main() {" - " o_col = texture(Tex, uv) * col;" - //" if (pixel_pos.x < ClipRect.x || pixel_pos.y < ClipRect.y || pixel_pos.x > ClipRect.z || pixel_pos.y > ClipRect.w) discard;" // Clipping: using discard - //" if (step(ClipRect.x,pixel_pos.x) * step(ClipRect.y,pixel_pos.y) * step(pixel_pos.x,ClipRect.z) * step(pixel_pos.y,ClipRect.w) < 1.0f) discard;" // Clipping: using discard and step - " o_col.w *= (step(ClipRect.x,pixel_pos.x) * step(ClipRect.y,pixel_pos.y) * step(pixel_pos.x,ClipRect.z) * step(pixel_pos.y,ClipRect.w));" // Clipping: branch-less, set alpha 0.0f - "}"; - // GLFW callbacks to get events static void glfw_error_callback(int error, const char* description) { - fputs(description, stderr); + fputs(description, stderr); } static float mouse_wheel = 0.0f; @@ -169,29 +113,17 @@ void InitGL() { glfwSetErrorCallback(glfw_error_callback); - if (!glfwInit()) - exit(1); + if (!glfwInit()) + exit(1); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - glfwWindowHint(GLFW_REFRESH_RATE, 60); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); window = glfwCreateWindow(1280, 720, "ImGui OpenGL example", NULL, NULL); glfwMakeContextCurrent(window); - glfwSetKeyCallback(window, glfw_key_callback); glfwSetScrollCallback(window, glfw_scroll_callback); glfwSetCharCallback(window, glfw_char_callback); - glewExperimental = GL_TRUE; glewInit(); - - // After calling glewInit() our GL error state may be GL_INVALID_ENUM - const GLenum err = glGetError(); - (void)err; - IM_ASSERT(err == GL_NO_ERROR || err == GL_INVALID_ENUM); } void InitImGui() @@ -200,10 +132,10 @@ void InitImGui() glfwGetWindowSize(window, &w, &h); ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize = ImVec2((float)w, (float)h); // Display size, in pixels. For clamping windows positions. - io.DeltaTime = 1.0f/60.0f; // Time elapsed since last frame, in seconds (in this sample app we'll override this every frame because our timestep is variable) - io.PixelCenterOffset = 0.5f; // Align OpenGL texels - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. + io.DisplaySize = ImVec2((float)w, (float)h); // Display size, in pixels. For clamping windows positions. + io.DeltaTime = 1.0f/60.0f; // Time elapsed since last frame, in seconds (in this sample app we'll override this every frame because our timestep is variable) + io.PixelCenterOffset = 0.5f; // Align OpenGL texels + io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; @@ -225,68 +157,11 @@ void InitImGui() io.SetClipboardTextFn = ImImpl_SetClipboardTextFn; io.GetClipboardTextFn = ImImpl_GetClipboardTextFn; - // Setup graphics backend - GLint status = GL_TRUE; - GLenum err = GL_NO_ERROR; - err = glGetError(); IM_ASSERT(err == GL_NO_ERROR); - - // Create and compile the vertex shader - vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, &vertexSource, NULL); - glCompileShader(vertexShader); - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) - { - char buffer[512]; - glGetShaderInfoLog(vertexShader, 1024, NULL, buffer); - printf("%s", buffer); - IM_ASSERT(status == GL_TRUE); - } - - // Create and compile the fragment shader - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, &fragmentSource, NULL); - glCompileShader(fragmentShader); - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status); - IM_ASSERT(status == GL_TRUE); - - // Link the vertex and fragment shader into a shader program - shaderProgram = glCreateProgram(); - glAttachShader(shaderProgram, vertexShader); - glAttachShader(shaderProgram, fragmentShader); - glBindFragDataLocation(shaderProgram, 0, "o_col"); - glLinkProgram(shaderProgram); - glGetProgramiv(shaderProgram, GL_LINK_STATUS, &status); - IM_ASSERT(status == GL_TRUE); - - // Create Vertex Buffer Objects & Vertex Array Objects - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - GLint posAttrib = glGetAttribLocation(shaderProgram, "i_pos"); - glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), 0); - glEnableVertexAttribArray(posAttrib); - - GLint uvAttrib = glGetAttribLocation(shaderProgram, "i_uv"); - glEnableVertexAttribArray(uvAttrib); - glVertexAttribPointer(uvAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (void*)(2*sizeof(float))); - - GLint colAttrib = glGetAttribLocation(shaderProgram, "i_col"); - glVertexAttribPointer(colAttrib, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (void*)(4*sizeof(float))); - glEnableVertexAttribArray(colAttrib); - err = glGetError(); IM_ASSERT(err == GL_NO_ERROR); - - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - // Load font texture glGenTextures(1, &fontTex); glBindTexture(GL_TEXTURE_2D, fontTex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); @@ -299,13 +174,6 @@ void InitImGui() void Shutdown() { ImGui::Shutdown(); - - glDeleteProgram(shaderProgram); - glDeleteShader(fragmentShader); - glDeleteShader(vertexShader); - glDeleteBuffers(1, &vbo); - glDeleteVertexArrays(1, &vao); - glfwTerminate(); } @@ -326,10 +194,10 @@ int main(int argc, char** argv) time = current_time; double mouse_x, mouse_y; glfwGetCursorPos(window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) io.MouseDown[0] = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) != 0; io.MouseDown[1] = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) != 0; - io.MouseWheel = (mouse_wheel != 0) ? mouse_wheel > 0.0f ? 1 : - 1 : 0; // Mouse wheel: -1,0,+1 + io.MouseWheel = (mouse_wheel != 0) ? mouse_wheel > 0.0f ? 1 : - 1 : 0; // Mouse wheel: -1,0,+1 mouse_wheel = 0.0f; ImGui::NewFrame(); @@ -356,7 +224,7 @@ int main(int argc, char** argv) if (show_test_window) { // More example code in ShowTestWindow() - ImGui::SetNewWindowDefaultPos(ImVec2(650, 20)); // Normally user code doesn't need/want to call it because positions are saved in .ini file anyway. Here we just want to make the demo initial state a bit more friendly! + ImGui::SetNewWindowDefaultPos(ImVec2(650, 20)); // Normally user code doesn't need/want to call it because positions are saved in .ini file anyway. Here we just want to make the demo initial state a bit more friendly! ImGui::ShowTestWindow(&show_test_window); } @@ -367,9 +235,10 @@ int main(int argc, char** argv) ImGui::End(); } - // 3) Rendering - glClearColor(0.8f, 0.6f, 0.6f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + // 3) Rendering + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + glClearColor(0.8f, 0.6f, 0.6f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); ImGui::Render(); glfwSwapBuffers(window);