Files
imgui-java/imgui-lwjgl3/src/test/java/ImGuiGlfwExample.java
T

276 lines
11 KiB
Java

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();
}
}