Skip to content

Getting Started

omar edited this page Jun 14, 2016 · 36 revisions

This article will guide you through getting up and running using GLFW with as many assumptions about your system as possible. ImGui is completely hardware/library agnostic and runs on everything, you only need to feed it with inputs and render textured triangles as output. As such, GLFW is merely used a convenient example because it is simple to use and popular.

image

You will learn

  • How this project is organised
  • How to open a window using GLFW
  • How to render an ImGui window into the GLFW window
  • How to render text into the ImGui window

You are

  • On Linux
  • Using C++11
  • Using OpenGL 3.3 or above, core profile
  • Using clang 3.4 or above
  • Using glfw 3.0 or above



Organisation

| File               | Description
|--------------------|--------------------
| imgui/             |
|   examples/        | Some examples for various backends and windowing libraries
|   extra_fonts/     | Optional assets
|   imconfig.h       | Configuration file
|   imgui.cpp        | 
|   imgui.h          | 
|   imgui_demo.cpp   | Demonstration code, useful to keep around, run and learn about imgui
|   imgui_draw.cpp   | 
|   imgui_internal.h | Internal details, without guarantee of backward compatibility
|   stb_*            | External library



Layout

This is the layout of your code.

| Directory           | Description
|---------------------|----------------------------------
| myproject/          |
|   build/            | Output goes here
|   include/          | Any include files you may eventually have
|   ext/              | External projects
|     glfw/           |
|     imgui/          |
|   src/              |
|     main.cpp        | Your single source file
|   build.sh          | Your build script
$ cd myproject/ext
$ git clone https://github.com/ocornut/imgui.git
$ git clone https://github.com/glfw/glfw.git



Build

Here is how your code will be built.

$ cd myproject
$ ./build.sh

build.sh

#!/usr/bin/env bash

mkdir -p build && \
pushd build && \
clang++ \
	-o main \
	../ext/imgui/imgui.cpp \
	../ext/imgui/imgui_draw.cpp \
	../src/main.cpp \
	-Wall \
	-g 						`# Generate complete debug info` \
	-std=c++11 \
	-I ../include \
	-I ../ext/glfw/include \
	-I ../ext/imgui \
	-L ../lib \
	-lglfw3 \
	-lGL \
	-lGLU \
	-lX11 \
	-lXxf86vm \
	-lXrandr \
	-lpthread \
	-lXi \
	-lXinerama \
  	-lXcursor && \
popd || exit 1



Source

Here is the source.

main.cpp

// dear imgui minimal example.

#include <cstdlib>
#include "GLFW/glfw3.h"
#include "imgui.h"

void setup(GLFWwindow* window);
void renderer(ImDrawData* draw_data);

int main(void)
{
    if (!glfwInit())
    {
        exit(EXIT_FAILURE);
    }

    static GLFWwindow* window {
        glfwCreateWindow(320, 240, "Simple example", NULL, NULL) 
    };

    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(window);

    // Close window on pressing ESC
    glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods) {
        if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
            glfwSetWindowShouldClose(window, GL_TRUE);
    });

    setup(window);

    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    while (!glfwWindowShouldClose(window))
    {
        glClear(GL_COLOR_BUFFER_BIT);

        glfwPollEvents();

        // Use ImGui functions between here and Render()
        ImGui::NewFrame();

        // This creates a window
        ImGui::Begin("Window Title Here");
        ImGui::Text("Hello, world!");
        ImGui::End();

        // ImGui functions end here
        ImGui::Render();

        glfwSwapBuffers(window);
    }

    glfwDestroyWindow(window);
    glfwTerminate();

    exit(EXIT_SUCCESS);
}

/*! /brief Initialise the ImGuiIO struct.
 *
 *   The ImGuiIO struct is the main configuration and
 *   I/O between your application and ImGui.
 */
void setup(GLFWwindow* window)
{
    unsigned char* pixels;
    int width,
        height,
        display_width,
        display_height;
    GLuint g_FontTexture;

    ImGuiIO& io { ImGui::GetIO() };

    io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);

    // 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);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
    
    // Get display size
    glfwGetWindowSize(window, &width, &height);
    glfwGetFramebufferSize(window, &display_width, &display_height);

    io.DisplaySize = ImVec2((float)width, (float)height);
    io.RenderDrawListsFn = renderer;
    io.Fonts->TexID = (void *)(intptr_t)g_FontTexture;

    // Restore state
    glBindTexture(GL_TEXTURE_2D, last_texture);
}

/*! /brief Boilerplate function for OpenGL 2.0 rendering.
 *
 *  This function isn't written by us, but is mandatory
 *  boilerplate from the library. It can be copy/pasted
 *  into your projects, but should really be part of the
 *  library itself?
 */
void renderer(ImDrawData* draw_data)
{
    ImGuiIO& io { ImGui::GetIO() };
    int fb_width { (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x) };
    int fb_height { (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y) };

    draw_data->ScaleClipRects(io.DisplayFramebufferScale);

    GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
    GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
    glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT);
    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);
    glEnable(GL_TEXTURE_2D);

    // Setup viewport, orthographic projection matrix
    glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    // Render command lists
    #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
    for (int n = 0; n < draw_data->CmdListsCount; n++)
    {
        const ImDrawList* cmd_list = draw_data->CmdLists[n];
        const unsigned char* vtx_buffer = (const unsigned char*)&cmd_list->VtxBuffer.front();
        const ImDrawIdx* idx_buffer = &cmd_list->IdxBuffer.front();
        glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer + OFFSETOF(ImDrawVert, pos)));
        glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer + OFFSETOF(ImDrawVert, uv)));
        glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (void*)(vtx_buffer + OFFSETOF(ImDrawVert, col)));

        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.size(); cmd_i++)
        {
            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
            if (pcmd->UserCallback)
            {
                pcmd->UserCallback(cmd_list, pcmd);
            }
            else
            {
                glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
                glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
                glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer);
            }
            idx_buffer += pcmd->ElemCount;
        }
    }
    #undef OFFSETOF

    // Restore modified state
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    glBindTexture(GL_TEXTURE_2D, last_texture);
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glPopAttrib();
    glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
}



TODO

Here are some ideas for how to improve and shorten this article.

  • Add screenshot of result
  • Reduce requirements, such as C++11
  • Increase compatibility, such as including a cl.exe build script.
  • Remove lines that aren't absolutely critical
  • Remove renderer() - Seems unnecessary and better included with ImGui?
  • Remove need to manually upload texture to GPU in setup(), can/should be included in ImGui?
Clone this wiki locally