Dear ImGui is a popular C++ GUI library. It can be used as an alternative for Scene2D. Since it is written in C++, you need to choose one of its Java bindings for using it with libGDX: kotlin-graphics/imgui, ice1000/jimgui, SpaiR/imgui-java and xpenatan/gdx-imgui.
General Information
Dear ImGui is designed to enable fast iteration and empower programmers to create content creation tools and visualization/debug tools (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal, and thus lacks certain features normally found in more high-level libraries.
A common misunderstanding is to think that immediate mode gui == immediate mode rendering, which usually implies hammering your driver/GPU with a bunch of inefficient draw calls and state changes, as the gui functions are called by the user. This is NOT what Dear ImGui does. Dear ImGui outputs vertex buffers and a small list of draw calls batches. It never touches your GPU directly. The draw call batches are decently optimal and you can render them later, in your app or even remotely.
This is an example demonstrating what ImGui is capable of:
Option 1: kotlin-graphics’ Bindings
There is an elaborate wiki entry over in the Kotlin-graphics’s repo, detailing how ImGui can be used together with libGDX: https://github.com/kotlin-graphics/imgui/wiki/Using-libGDX
These are some very simple examples, how its usage may look like:
Kotlin
var f = 0f
with(ImGui) {
text("Hello, world %d", 123)
button("OK"){
// react
}
inputText("string", buf)
sliderFloat("float", ::f, 0f, 1f)
}
Java
ImGui imgui = ImGui.INSTANCE;
imgui.text("Hello, world %d", 123);
if(imgui.button("OK")) {
// react
}
imgui.inputText("string", buf);
float[] f = {0f};
imgui.sliderFloat("float", f, 0f, 1f);
ImGui also supports other languages, such as Japanese, initiliazed here as:
IO.fonts.addFontFromFileTTF("extraFonts/ArialUni.ttf", 18f, glyphRanges = IO.fonts.glyphRangesJapanese)!!
Option 2: SpaiR’s Bindings
Required Dependencies for the LWJGL 3 Subproject
api "io.github.spair:imgui-java-binding:<version>"
api "io.github.spair:imgui-java-lwjgl3:<version>"
api "io.github.spair:imgui-java-natives-linux:<version>"
api "io.github.spair:imgui-java-natives-macos:<version>"
api "io.github.spair:imgui-java-natives-windows:<version>"
Replace <version>
with the latest version found at the top of the README file here.
Example Minimal Usage
The following instructions detail how ImGui can be used in a libGDX game. For easier use, you can put most of the methods in a specialised class, e.g. ImGuiRenderer
.
-
Initialize ImGui. This has to be done once for your application, for example in
Game#create()
:private static ImGuiImplGlfw imGuiGlfw; private static ImGuiImplGl3 imGuiGl3; public static void init() { imGuiGlfw = new ImGuiImplGlfw(); imGuiGl3 = new ImGuiImplGl3(); long windowHandle = ((Lwjgl3Graphics) Gdx.graphics).getWindow().getWindowHandle(); ImGui.createContext(); ImGuiIO io = ImGui.getIO(); io.setIniFilename(null); io.getFonts().addFontDefault(); io.getFonts().build(); imGuiGlfw.init(windowHandle, true); imGuiGl3.init("#version 150"); }
-
Before you start using any of the ImGui methods in the
render()
method of your screens, you need to start a new frame:private static InputProcessor tmpProcessor; public static void start() { if (tmpProcessor != null) { // Restore the input processor after ImGui caught all inputs, see #end() Gdx.input.setInputProcessor(tmpProcessor); tmpProcessor = null; } imGuiGlfw.newFrame(); ImGui.newFrame(); }
-
Then you can render any UI stuff you want, e.g.:
ImGui.button("I'm a Button!");
-
Afterwards, you need to actually render the ImGui frame:
public static void end() { ImGui.render(); imGuiGl3.renderDrawData(ImGui.getDrawData()); // If ImGui wants to capture the input, disable libGDX's input processor if (ImGui.getIO().getWantCaptureKeyboard() || ImGui.getIO().getWantCaptureMouse()) { tmpProcessor = Gdx.input.getInputProcessor(); Gdx.input.setInputProcessor(null); } }
-
Be sure to dispose ImGui if you no longer need it:
public static void dispose() { imGuiGl3.dispose(); imGuiGl3 = null; imGuiGlfw.dispose(); imGuiGlfw = null; ImGui.destroyContext(); }
-
The result can look like this: