Add port of GLFW backend and use in GLFW example (#13)

This commit is contained in:
Ashley Bye authored and GitHub committed 2020-06-28 17:11:42 +03:00
1 parent ff4ad75205
commit 0b478fd337
2 files changed
+480 -173

No files matched your search

+66 -173
View File
@@ -1,35 +1,56 @@
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.callback.ImStrConsumer;
import imgui.callback.ImStrSupplier;
import imgui.flag.ImGuiBackendFlags;
import imgui.flag.ImGuiConfigFlags;
import imgui.flag.ImGuiKey;
import imgui.flag.ImGuiMouseCursor;
import imgui.gl3.ImGuiImplGl3;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryStack;
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 static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL32.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;
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
@@ -45,6 +66,9 @@ public final class ImGuiGlfwExample {
// 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
@@ -53,17 +77,34 @@ public final class ImGuiGlfwExample {
private final ExampleUi exampleUi = new ExampleUi();
public void run() throws Exception {
initGlfw();
initImGui();
// 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();
destroyImGui();
destroyGlfw();
// 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 initGlfw() {
private void setupGlfw() {
// Setup an error callback. The default implementation
// will print the error message in System.err.
GLFWErrorCallback.createPrint(System.err).set();
@@ -129,11 +170,7 @@ public final class ImGuiGlfwExample {
}
// Initialize Dear ImGui.
private void initImGui() {
// IMPORTANT!!
// This line is critical for Dear ImGui to work.
ImGui.createContext();
private void setupImGui() {
// ------------------------------------------------------------
// Initialize ImGuiIO config
final ImGuiIO io = ImGui.getIO();
@@ -143,103 +180,6 @@ public final class ImGuiGlfwExample {
io.setBackendFlags(ImGuiBackendFlags.HasMouseCursors); // Mouse cursors to display while resizing windows etc.
io.setBackendPlatformName("imgui_java_impl_glfw");
// ------------------------------------------------------------
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
final int[] keyMap = new int[ImGuiKey.COUNT];
keyMap[ImGuiKey.Tab] = GLFW_KEY_TAB;
keyMap[ImGuiKey.LeftArrow] = GLFW_KEY_LEFT;
keyMap[ImGuiKey.RightArrow] = GLFW_KEY_RIGHT;
keyMap[ImGuiKey.UpArrow] = GLFW_KEY_UP;
keyMap[ImGuiKey.DownArrow] = GLFW_KEY_DOWN;
keyMap[ImGuiKey.PageUp] = GLFW_KEY_PAGE_UP;
keyMap[ImGuiKey.PageDown] = GLFW_KEY_PAGE_DOWN;
keyMap[ImGuiKey.Home] = GLFW_KEY_HOME;
keyMap[ImGuiKey.End] = GLFW_KEY_END;
keyMap[ImGuiKey.Insert] = GLFW_KEY_INSERT;
keyMap[ImGuiKey.Delete] = GLFW_KEY_DELETE;
keyMap[ImGuiKey.Backspace] = GLFW_KEY_BACKSPACE;
keyMap[ImGuiKey.Space] = GLFW_KEY_SPACE;
keyMap[ImGuiKey.Enter] = GLFW_KEY_ENTER;
keyMap[ImGuiKey.Escape] = GLFW_KEY_ESCAPE;
keyMap[ImGuiKey.KeyPadEnter] = GLFW_KEY_KP_ENTER;
keyMap[ImGuiKey.A] = GLFW_KEY_A;
keyMap[ImGuiKey.C] = GLFW_KEY_C;
keyMap[ImGuiKey.V] = GLFW_KEY_V;
keyMap[ImGuiKey.X] = GLFW_KEY_X;
keyMap[ImGuiKey.Y] = GLFW_KEY_Y;
keyMap[ImGuiKey.Z] = GLFW_KEY_Z;
io.setKeyMap(keyMap);
// ------------------------------------------------------------
// Mouse cursors mapping
mouseCursors[ImGuiMouseCursor.Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
mouseCursors[ImGuiMouseCursor.TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
mouseCursors[ImGuiMouseCursor.ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
mouseCursors[ImGuiMouseCursor.ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
mouseCursors[ImGuiMouseCursor.ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
mouseCursors[ImGuiMouseCursor.ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
mouseCursors[ImGuiMouseCursor.ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
mouseCursors[ImGuiMouseCursor.Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
mouseCursors[ImGuiMouseCursor.NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
// ------------------------------------------------------------
// GLFW callbacks to handle user input
glfwSetKeyCallback(windowPtr, (w, key, scancode, action, mods) -> {
if (action == GLFW_PRESS) {
io.setKeysDown(key, true);
} else if (action == GLFW_RELEASE) {
io.setKeysDown(key, false);
}
io.setKeyCtrl(io.getKeysDown(GLFW_KEY_LEFT_CONTROL) || io.getKeysDown(GLFW_KEY_RIGHT_CONTROL));
io.setKeyShift(io.getKeysDown(GLFW_KEY_LEFT_SHIFT) || io.getKeysDown(GLFW_KEY_RIGHT_SHIFT));
io.setKeyAlt(io.getKeysDown(GLFW_KEY_LEFT_ALT) || io.getKeysDown(GLFW_KEY_RIGHT_ALT));
io.setKeySuper(io.getKeysDown(GLFW_KEY_LEFT_SUPER) || io.getKeysDown(GLFW_KEY_RIGHT_SUPER));
});
glfwSetCharCallback(windowPtr, (w, c) -> {
if (c != GLFW_KEY_DELETE) {
io.addInputCharacter(c);
}
});
glfwSetMouseButtonCallback(windowPtr, (w, button, action, mods) -> {
io.setMouseDown(0, button == GLFW_MOUSE_BUTTON_1 && action != GLFW_RELEASE);
io.setMouseDown(1, button == GLFW_MOUSE_BUTTON_2 && action != GLFW_RELEASE);
io.setMouseDown(2, button == GLFW_MOUSE_BUTTON_3 && action != GLFW_RELEASE);
io.setMouseDown(3, button == GLFW_MOUSE_BUTTON_4 && action != GLFW_RELEASE);
io.setMouseDown(4, button == GLFW_MOUSE_BUTTON_5 && action != GLFW_RELEASE);
if (!io.getWantCaptureMouse() && io.getMouseDown(1)) {
ImGui.setWindowFocus(null);
}
});
glfwSetScrollCallback(windowPtr, (w, xOffset, yOffset) -> {
io.setMouseWheelH(io.getMouseWheelH() + (float) xOffset);
io.setMouseWheel(io.getMouseWheel() + (float) yOffset);
});
io.setSetClipboardTextFn(new ImStrConsumer() {
@Override
public void accept(final String s) {
glfwSetClipboardString(windowPtr, s);
}
});
io.setGetClipboardTextFn(new ImStrSupplier() {
@Override
public String get() {
final String clipboardString = glfwGetClipboardString(windowPtr);
if (clipboardString != null) {
return clipboardString;
} else {
return "";
}
}
});
// ------------------------------------------------------------
// Fonts configuration
// Read: https://raw.githubusercontent.com/ocornut/imgui/master/docs/FONTS.txt
@@ -278,28 +218,18 @@ public final class ImGuiGlfwExample {
// ------------------------------------------------------------
// Use freetype instead of stb_truetype to build a fonts texture
ImGuiFreeType.buildFontAtlas(fontAtlas, ImGuiFreeType.RasterizerFlags.LightHinting);
// 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.
imGuiGl3.init(glslVersion);
}
// Main application loop
private void loop() {
double time = 0; // to track our frame delta value
// Run the rendering loop until the user has attempted to close the window
while (!glfwWindowShouldClose(windowPtr)) {
// Count frame delta value
final double currentTime = glfwGetTime();
final double deltaTime = (time > 0) ? (currentTime - time) : 1f / 60f;
time = currentTime;
startFrame((float) deltaTime);
startFrame();
// Any Dear ImGui code SHOULD go between ImGui.newFrame()/ImGui.render() methods
imGuiGlfw.newFrame();
ImGui.newFrame();
exampleUi.render();
ImGui.render();
@@ -307,30 +237,10 @@ public final class ImGuiGlfwExample {
}
}
private void startFrame(final float deltaTime) {
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);
// Get window properties and mouse position
glfwGetWindowSize(windowPtr, winWidth, winHeight);
glfwGetFramebufferSize(windowPtr, fbWidth, fbHeight);
glfwGetCursorPos(windowPtr, mousePosX, mousePosY);
final float scaleX = (float) fbWidth[0] / winWidth[0];
final float scaleY = (float) fbHeight[0] / winHeight[0];
// We SHOULD call those methods to update Dear ImGui state for the current frame
final ImGuiIO io = ImGui.getIO();
io.setDisplaySize(fbWidth[0], fbHeight[0]);
io.setDisplayFramebufferScale(scaleX, scaleY);
io.setMousePos((float) mousePosX[0] * scaleX, (float) mousePosY[0] * scaleY);
io.setDeltaTime(deltaTime);
// Update the mouse cursor
final int imguiCursor = ImGui.getMouseCursor();
glfwSetCursor(windowPtr, mouseCursors[imguiCursor]);
glfwSetInputMode(windowPtr, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
private void endFrame() {
@@ -342,26 +252,9 @@ public final class ImGuiGlfwExample {
glfwPollEvents();
}
// If you want to clean a room after yourself - do it by yourself
private void destroyImGui() {
imGuiGl3.dispose();
ImGui.destroyContext();
}
private void destroyGlfw() {
for (long mouseCursor : mouseCursors) {
glfwDestroyCursor(mouseCursor);
}
glfwFreeCallbacks(windowPtr);
glfwDestroyWindow(windowPtr);
glfwTerminate();
Objects.requireNonNull(glfwSetErrorCallback(null)).free();
}
private byte[] loadFromResources(final String fileName) {
try (InputStream is = Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream(fileName));
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
final byte[] data = new byte[16384];