import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR; import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR; import static org.lwjgl.glfw.GLFW.GLFW_FALSE; import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE; import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_FORWARD_COMPAT; import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE; import static org.lwjgl.glfw.GLFW.GLFW_TRUE; import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE; import static org.lwjgl.glfw.GLFW.glfwCreateWindow; import static org.lwjgl.glfw.GLFW.glfwDefaultWindowHints; import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor; import static org.lwjgl.glfw.GLFW.glfwGetVideoMode; import static org.lwjgl.glfw.GLFW.glfwGetWindowSize; import static org.lwjgl.glfw.GLFW.glfwInit; import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent; import static org.lwjgl.glfw.GLFW.glfwPollEvents; import static org.lwjgl.glfw.GLFW.glfwSetWindowPos; import static org.lwjgl.glfw.GLFW.glfwShowWindow; import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; import static org.lwjgl.glfw.GLFW.glfwSwapInterval; import static org.lwjgl.glfw.GLFW.glfwWindowHint; import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose; import static org.lwjgl.opengl.GL32.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL32.GL_TRUE; import static org.lwjgl.opengl.GL32.glClear; import static org.lwjgl.opengl.GL32.glClearColor; import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.system.MemoryUtil.NULL; import imgui.ImFontAtlas; import imgui.ImFontConfig; import imgui.ImGui; import imgui.ImGuiFreeType; import imgui.ImGuiIO; import imgui.flag.ImGuiBackendFlags; import imgui.flag.ImGuiConfigFlags; import imgui.flag.ImGuiMouseCursor; import imgui.gl3.ImGuiImplGl3; import imgui.glfw.ImGuiImplGlfw; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.IntBuffer; import java.util.Objects; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; import org.lwjgl.system.MemoryStack; @SuppressWarnings("MagicNumber") public final class ImGuiGlfwExample { private long windowPtr; // pointer to the current GLFW window // For application window properties private final int[] winWidth = new int[1]; private final int[] winHeight = new int[1]; private final int[] fbWidth = new int[1]; private final int[] fbHeight = new int[1]; // For mouse tracking private final double[] mousePosX = new double[1]; private final double[] mousePosY = new double[1]; // Mouse cursors provided by GLFW private final long[] mouseCursors = new long[ImGuiMouseCursor.COUNT]; // LWJGJ3 window backend private final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw(); // LWJGL3 renderer (SHOULD be initialized) private final ImGuiImplGl3 imGuiGl3 = new ImGuiImplGl3(); private String glslVersion = null; // We can initialize our renderer with different versions of the GLSL // User UI to render private final ExampleUi exampleUi = new ExampleUi(); public void run() throws Exception { // IMPORTANT!! // This line is critical for Dear ImGui to work. ImGui.createContext(); // Method initializes GLFW backend. // This method SHOULD be called after you've setup GLFW. // ImGui context should be created as well. setupGlfw(); imGuiGlfw.init(windowPtr, true); // Method initializes LWJGL3 renderer. // This method SHOULD be called after you've initialized your ImGui configuration (fonts and so on). // ImGui context should be created as well. setupImGui(); imGuiGl3.init(glslVersion); exampleUi.init(); loop(); // You should clean up after yourself in reverse order. imGuiGl3.dispose(); imGuiGlfw.dispose(); ImGui.destroyContext(); } // Initialize GLFW + create an OpenGL context. // All code is mostly a copy-paste from the official LWJGL3 "Get Started": https://www.lwjgl.org/guide private void setupGlfw() { // Setup an error callback. The default implementation // will print the error message in System.err. GLFWErrorCallback.createPrint(System.err).set(); // Initialize GLFW. Most GLFW functions will not work before doing this. if (!glfwInit()) { throw new IllegalStateException("Unable to initialize GLFW"); } // Configure GLFW glfwDefaultWindowHints(); // optional, the current window hints are already the default glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation decideGlGlslVersions(); windowPtr = glfwCreateWindow(1280, 768, "Dear ImGui+LWJGL Example", NULL, NULL); if (windowPtr == NULL) { throw new RuntimeException("Failed to create the GLFW window"); } // Get the thread stack and push a new frame try (MemoryStack stack = stackPush()) { final IntBuffer pWidth = stack.mallocInt(1); // int* final IntBuffer pHeight = stack.mallocInt(1); // int* // Get the window size passed to glfwCreateWindow glfwGetWindowSize(windowPtr, pWidth, pHeight); // Get the resolution of the primary monitor final GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); // Center the window glfwSetWindowPos(windowPtr, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2); } // the stack frame is popped automatically glfwMakeContextCurrent(windowPtr); // Make the OpenGL context current glfwSwapInterval(GLFW_TRUE); // Enable v-sync glfwShowWindow(windowPtr); // Make the window visible // IMPORTANT!! // This line is critical for LWJGL's interoperation with GLFW's // OpenGL context, or any context that is managed externally. // LWJGL detects the context that is current in the current thread, // creates the GLCapabilities instance and makes the OpenGL // bindings available for use. GL.createCapabilities(); } private void decideGlGlslVersions() { final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac"); if (isMac) { glslVersion = "#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 { glslVersion = "#version 130"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); } } // Initialize Dear ImGui. private void setupImGui() { // ------------------------------------------------------------ // Initialize ImGuiIO config final ImGuiIO io = ImGui.getIO(); io.setIniFilename(null); // We don't want to save .ini file io.setConfigFlags(ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable); // Navigation with keyboard and enabled docking io.setBackendFlags(ImGuiBackendFlags.HasMouseCursors); // Mouse cursors to display while resizing windows etc. io.setBackendPlatformName("imgui_java_impl_glfw"); // ------------------------------------------------------------ // Fonts configuration // Read: https://raw.githubusercontent.com/ocornut/imgui/master/docs/FONTS.txt final ImFontAtlas fontAtlas = io.getFonts(); final ImFontConfig fontConfig = new ImFontConfig(); // Natively allocated object, should be explicitly destroyed // Glyphs could be added per-font as well as per config used globally like here fontConfig.setGlyphRanges(fontAtlas.getGlyphRangesCyrillic()); // Add a default font, which is 'ProggyClean.ttf, 13px' fontAtlas.addFontDefault(); // Fonts merge example fontConfig.setMergeMode(true); // When enabled, all fonts added with this config would be merged with the previously added font fontConfig.setPixelSnapH(true); fontAtlas.addFontFromMemoryTTF(loadFromResources("basis33.ttf"), 16, fontConfig); fontConfig.setMergeMode(false); fontConfig.setPixelSnapH(false); // Fonts from file/memory example // We can add new fonts from the file system fontAtlas.addFontFromFileTTF("src/test/resources/Righteous-Regular.ttf", 14, fontConfig); fontAtlas.addFontFromFileTTF("src/test/resources/Righteous-Regular.ttf", 16, fontConfig); // Or directly from the memory fontConfig.setName("Roboto-Regular.ttf, 14px"); // This name will be displayed in Style Editor fontAtlas.addFontFromMemoryTTF(loadFromResources("Roboto-Regular.ttf"), 14, fontConfig); fontConfig.setName("Roboto-Regular.ttf, 16px"); // We can apply a new config value every time we add a new font fontAtlas.addFontFromMemoryTTF(loadFromResources("Roboto-Regular.ttf"), 16, fontConfig); fontConfig.destroy(); // After all fonts were added we don't need this config more // ------------------------------------------------------------ // Use freetype instead of stb_truetype to build a fonts texture ImGuiFreeType.buildFontAtlas(fontAtlas, ImGuiFreeType.RasterizerFlags.LightHinting); } // Main application loop private void loop() { // Run the rendering loop until the user has attempted to close the window while (!glfwWindowShouldClose(windowPtr)) { startFrame(); // Any Dear ImGui code SHOULD go between ImGui.newFrame()/ImGui.render() methods imGuiGlfw.newFrame(); ImGui.newFrame(); exampleUi.render(); ImGui.render(); endFrame(); } } private void startFrame() { // Set the clear color and clear the window glClearColor(exampleUi.backgroundColor[0], exampleUi.backgroundColor[1], exampleUi.backgroundColor[2], 0.0f); glClear(GL_COLOR_BUFFER_BIT); } private void endFrame() { // After Dear ImGui prepared a draw data, we use it in the LWJGL3 renderer. // At that moment ImGui will be rendered to the current OpenGL context. imGuiGl3.render(ImGui.getDrawData()); glfwSwapBuffers(windowPtr); glfwPollEvents(); } private byte[] loadFromResources(final String fileName) { try (InputStream is = Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream(fileName)); ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { final byte[] data = new byte[16384]; int nRead; while ((nRead = is.read(data, 0, data.length)) != -1) { buffer.write(data, 0, nRead); } return buffer.toByteArray(); } catch (IOException e) { throw new UncheckedIOException(e); } } public static void main(final String[] args) throws Exception { new ImGuiGlfwExample().run(); } }