Add port of GLFW backend and use in GLFW example (#13)
This commit is contained in:
1 parent
ff4ad75205
commit
0b478fd337
2 files changed
+480
-173
No files matched your search
@@ -0,0 +1,414 @@
|
||||
package imgui.glfw;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_ARROW_CURSOR;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_CURSOR;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_HIDDEN;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_NORMAL;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_FOCUSED;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_HAND_CURSOR;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_HRESIZE_CURSOR;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_IBEAM_CURSOR;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_JOYSTICK_1;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_A;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_BACKSPACE;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_C;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_DELETE;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_DOWN;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_END;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_ENTER;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_ESCAPE;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_HOME;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_INSERT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_ENTER;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_ALT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SUPER;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_PAGE_DOWN;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_PAGE_UP;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_ALT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_CONTROL;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SHIFT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SUPER;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_TAB;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_UP;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_V;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_X;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_Y;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_KEY_Z;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_PRESS;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_RELEASE;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_VRESIZE_CURSOR;
|
||||
import static org.lwjgl.glfw.GLFW.glfwCreateStandardCursor;
|
||||
import static org.lwjgl.glfw.GLFW.glfwDestroyCursor;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetClipboardString;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetCursorPos;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetFramebufferSize;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetInputMode;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetJoystickAxes;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetJoystickButtons;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetMouseButton;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetTime;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetWindowAttrib;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetWindowSize;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetCharCallback;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetClipboardString;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetCursor;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetCursorPos;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetInputMode;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetKeyCallback;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetMouseButtonCallback;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetScrollCallback;
|
||||
|
||||
import imgui.ImGui;
|
||||
import imgui.ImGuiIO;
|
||||
import imgui.ImVec2;
|
||||
import imgui.callback.ImStrConsumer;
|
||||
import imgui.callback.ImStrSupplier;
|
||||
import imgui.flag.ImGuiBackendFlags;
|
||||
import imgui.flag.ImGuiConfigFlags;
|
||||
import imgui.flag.ImGuiKey;
|
||||
import imgui.flag.ImGuiMouseButton;
|
||||
import imgui.flag.ImGuiMouseCursor;
|
||||
import imgui.flag.ImGuiNavInput;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import org.lwjgl.glfw.GLFWCharCallback;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWKeyCallback;
|
||||
import org.lwjgl.glfw.GLFWMouseButtonCallback;
|
||||
import org.lwjgl.glfw.GLFWScrollCallback;
|
||||
|
||||
/**
|
||||
* This class is a straightforward port of the
|
||||
* <a href="https://raw.githubusercontent.com/ocornut/imgui/v1.76/examples/imgui_impl_glfw.cpp">imgui_impl_glfw.cpp</a>.
|
||||
* <p>
|
||||
* It supports clipboard, gamepad, mouse and keyboard in the same way the original Dear ImGui code does. You can copy-paste this class in your codebase and
|
||||
* modify the rendering routine in the way you'd like.
|
||||
* <p>
|
||||
*/
|
||||
public class ImGuiImplGlfw {
|
||||
|
||||
// Id of the current GLFW window
|
||||
private long windowId;
|
||||
|
||||
// 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];
|
||||
|
||||
// Mouse cursors provided by GLFW
|
||||
private final long[] mouseCursors = new long[ImGuiMouseCursor.COUNT];
|
||||
|
||||
// For mouse tracking
|
||||
private final boolean[] mouseJustPressed = new boolean[ImGuiMouseButton.COUNT];
|
||||
private final double[] cursorPosX = new double[1];
|
||||
private final double[] cursorPosY = new double[1];
|
||||
|
||||
// GLFW callbacks
|
||||
private GLFWMouseButtonCallback previousMouseButtonCallback = null;
|
||||
private GLFWScrollCallback previousScrollCallback = null;
|
||||
private GLFWKeyCallback previousKeyCallback = null;
|
||||
private GLFWCharCallback previousCharCallback = null;
|
||||
|
||||
private boolean callbacksInstalled;
|
||||
private double time = 0.0;
|
||||
|
||||
/**
|
||||
* Method to set the {@link GLFWMouseButtonCallback}.
|
||||
*/
|
||||
public void mouseButtonCallback(final long windowId, final int button, final int action, final int mods) {
|
||||
if (previousMouseButtonCallback != null) {
|
||||
previousMouseButtonCallback.invoke(windowId, button, action, mods);
|
||||
}
|
||||
|
||||
if (action == GLFW_PRESS && button >= 0 && button < mouseJustPressed.length) {
|
||||
mouseJustPressed[button] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the {@link GLFWScrollCallback}.
|
||||
*/
|
||||
public void scrollCallback(final long windowId, final double xOffset, final double yOffset) {
|
||||
if (previousScrollCallback != null) {
|
||||
previousScrollCallback.invoke(windowId, xOffset, yOffset);
|
||||
}
|
||||
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
io.setMouseWheelH((float) xOffset);
|
||||
io.setMouseWheel((float) yOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the {@link GLFWKeyCallback}.
|
||||
*/
|
||||
public void keyCallback(final long windowId, final int key, final int scancode, final int action, final int mods) {
|
||||
if (previousKeyCallback != null) {
|
||||
previousKeyCallback.invoke(windowId, key, scancode, action, mods);
|
||||
}
|
||||
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
if (action == GLFW_PRESS) {
|
||||
io.setKeysDown(key, true);
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the {@link GLFWCharCallback}.
|
||||
*/
|
||||
public void charCallback(final long windowId, final int c) {
|
||||
if (previousCharCallback != null) {
|
||||
previousCharCallback.invoke(windowId, c);
|
||||
}
|
||||
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
io.addInputCharacter(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to do an initialization of the {@link ImGuiImplGlfw} state. It SHOULD be called before calling the {@link ImGuiImplGlfw#newFrame()} method.
|
||||
* <p>
|
||||
* Method takes two arguments, which should be a valid GLFW window pointer and a boolean indicating whether or not to install callbacks.
|
||||
*/
|
||||
public boolean init(final long windowId, final boolean installCallbacks) {
|
||||
this.windowId = windowId;
|
||||
time = 0.0;
|
||||
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
|
||||
io.setBackendFlags(ImGuiBackendFlags.HasMouseCursors | ImGuiBackendFlags.HasSetMousePos);
|
||||
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);
|
||||
|
||||
io.setGetClipboardTextFn(new ImStrSupplier() {
|
||||
@Override
|
||||
public String get() {
|
||||
final String clipboardString = glfwGetClipboardString(windowId);
|
||||
return clipboardString != null ? clipboardString : "";
|
||||
}
|
||||
});
|
||||
|
||||
io.setSetClipboardTextFn(new ImStrConsumer() {
|
||||
@Override
|
||||
public void accept(final String str) {
|
||||
glfwSetClipboardString(windowId, str);
|
||||
}
|
||||
});
|
||||
|
||||
// Mouse cursors mapping. Disable errors whilst setting due to X11.
|
||||
final GLFWErrorCallback prevErrorCallback = glfwSetErrorCallback(null);
|
||||
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);
|
||||
glfwSetErrorCallback(prevErrorCallback);
|
||||
|
||||
if (installCallbacks) {
|
||||
callbacksInstalled = true;
|
||||
previousMouseButtonCallback = glfwSetMouseButtonCallback(windowId, this::mouseButtonCallback);
|
||||
previousScrollCallback = glfwSetScrollCallback(windowId, this::scrollCallback);
|
||||
previousKeyCallback = glfwSetKeyCallback(windowId, this::keyCallback);
|
||||
previousCharCallback = glfwSetCharCallback(windowId, this::charCallback);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to restore {@link org.lwjgl.glfw.GLFW} to it's state prior to calling method {@link ImGuiImplGlfw#init(long, boolean)}.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (callbacksInstalled) {
|
||||
glfwSetMouseButtonCallback(windowId, previousMouseButtonCallback);
|
||||
glfwSetScrollCallback(windowId, previousScrollCallback);
|
||||
glfwSetKeyCallback(windowId, previousKeyCallback);
|
||||
glfwSetCharCallback(windowId, previousCharCallback);
|
||||
callbacksInstalled = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ImGuiMouseCursor.COUNT; i++) {
|
||||
glfwDestroyCursor(mouseCursors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMousePosAndButtons(final float scaleX, final float scaleY) {
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
for (int i = 0; i < ImGuiMouseButton.COUNT; i++) {
|
||||
io.setMouseDown(i, mouseJustPressed[i] || glfwGetMouseButton(windowId, i) != 0);
|
||||
mouseJustPressed[i] = false;
|
||||
}
|
||||
|
||||
final ImVec2 mousePosBackup = new ImVec2();
|
||||
io.getMousePos(mousePosBackup);
|
||||
io.setMousePos(-Float.MAX_VALUE, -Float.MAX_VALUE);
|
||||
|
||||
final boolean focused = glfwGetWindowAttrib(windowId, GLFW_FOCUSED) != 0;
|
||||
if (focused) {
|
||||
if (io.getWantSetMousePos()) {
|
||||
glfwSetCursorPos(windowId, mousePosBackup.x, mousePosBackup.y);
|
||||
} else {
|
||||
glfwGetCursorPos(windowId, cursorPosX, cursorPosY);
|
||||
io.setMousePos((float) cursorPosX[0] * scaleX, (float) cursorPosY[0] * scaleY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMouseCursor() {
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
final boolean noCursorChange = (io.getConfigFlags() & ImGuiConfigFlags.NoMouseCursorChange)
|
||||
== ImGuiConfigFlags.NoMouseCursorChange;
|
||||
final boolean cursorDisabled = glfwGetInputMode(windowId, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
|
||||
if (noCursorChange || cursorDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int cursor = ImGui.getMouseCursor();
|
||||
if (cursor == ImGuiMouseCursor.None || io.getMouseDrawCursor()) {
|
||||
glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
} else {
|
||||
glfwSetCursor(windowId, mouseCursors[cursor] != 0 ? mouseCursors[cursor]
|
||||
: mouseCursors[ImGuiMouseCursor.Arrow]);
|
||||
glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateGamepads() {
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
|
||||
final float[] navInputs = new float[ImGuiNavInput.COUNT];
|
||||
io.setNavInputs(navInputs);
|
||||
|
||||
if ((io.getConfigFlags() & ImGuiConfigFlags.NavEnableGamepad) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ByteBuffer buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1);
|
||||
final int buttonsCount = buttons.limit();
|
||||
|
||||
final FloatBuffer axis = glfwGetJoystickAxes(GLFW_JOYSTICK_1);
|
||||
final int axisCount = axis.limit();
|
||||
|
||||
mapButton(ImGuiNavInput.Activate, 0, buttons, buttonsCount, io); // Cross / A
|
||||
mapButton(ImGuiNavInput.Cancel, 1, buttons, buttonsCount, io); // Circle / B
|
||||
mapButton(ImGuiNavInput.Menu, 2, buttons, buttonsCount, io); // Square / X
|
||||
mapButton(ImGuiNavInput.Input, 3, buttons, buttonsCount, io); // Triangle / Y
|
||||
mapButton(ImGuiNavInput.DpadLeft, 13, buttons, buttonsCount, io); // D-Pad Left
|
||||
mapButton(ImGuiNavInput.DpadRight, 11, buttons, buttonsCount, io); // D-Pad Right
|
||||
mapButton(ImGuiNavInput.DpadUp, 10, buttons, buttonsCount, io); // D-Pad Up
|
||||
mapButton(ImGuiNavInput.DpadDown, 12, buttons, buttonsCount, io); // D-Pad Down
|
||||
mapButton(ImGuiNavInput.FocusPrev, 4, buttons, buttonsCount, io); // L1 / LB
|
||||
mapButton(ImGuiNavInput.FocusNext, 5, buttons, buttonsCount, io); // R1 / RB
|
||||
mapButton(ImGuiNavInput.TweakSlow, 4, buttons, buttonsCount, io); // L1 / LB
|
||||
mapButton(ImGuiNavInput.TweakFast, 5, buttons, buttonsCount, io); // R1 / RB
|
||||
mapAnalog(ImGuiNavInput.LStickLeft, 0, -0.3f, -0.9f, axis, axisCount, io);
|
||||
mapAnalog(ImGuiNavInput.LStickRight, 0, +0.3f, +0.9f, axis, axisCount, io);
|
||||
mapAnalog(ImGuiNavInput.LStickUp, 1, +0.3f, +0.9f, axis, axisCount, io);
|
||||
mapAnalog(ImGuiNavInput.LStickDown, 1, -0.3f, -0.9f, axis, axisCount, io);
|
||||
|
||||
if (axisCount > 0 && buttonsCount > 0) {
|
||||
io.setBackendFlags(io.getBackendFlags() | ImGuiBackendFlags.HasGamepad);
|
||||
} else {
|
||||
io.setBackendFlags(io.getBackendFlags() & ~ImGuiBackendFlags.HasGamepad);
|
||||
}
|
||||
}
|
||||
|
||||
private void mapButton(final int navNo, final int buttonNo, final ByteBuffer buttons, final int buttonsCount, final ImGuiIO io) {
|
||||
if (buttonsCount > buttonNo && buttons.get(buttonNo) == GLFW_PRESS) {
|
||||
io.setNavInputs(navNo, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void mapAnalog(
|
||||
final int navNo,
|
||||
final int axisNo,
|
||||
final float v0,
|
||||
final float v1,
|
||||
final FloatBuffer axis,
|
||||
final int axisCount,
|
||||
final ImGuiIO io
|
||||
) {
|
||||
float v = axisCount > axisNo ? axis.get(axisNo) : v0;
|
||||
v = (v - v0) / (v1 - v0);
|
||||
if (v > 1.0f) {
|
||||
v = 1.0f;
|
||||
}
|
||||
if (io.getNavInputs(navNo) < v) {
|
||||
io.setNavInputs(navNo, v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates {@link ImGuiIO} and {@link org.lwjgl.glfw.GLFW} state.
|
||||
*/
|
||||
public void newFrame() {
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
if (!io.getFonts().isBuilt()) {
|
||||
throw new IllegalStateException(
|
||||
"Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer init() method? e.g. ImGuiImplGl3.init().");
|
||||
}
|
||||
|
||||
glfwGetWindowSize(windowId, winWidth, winHeight);
|
||||
glfwGetFramebufferSize(windowId, fbWidth, fbHeight);
|
||||
|
||||
final float scaleX = (float) fbWidth[0] / winWidth[0];
|
||||
final float scaleY = (float) fbHeight[0] / winHeight[0];
|
||||
|
||||
io.setDisplaySize((float) winWidth[0], (float) winHeight[0]);
|
||||
if (winWidth[0] > 0 && winHeight[0] > 0) {
|
||||
io.setDisplayFramebufferScale(scaleX, scaleY);
|
||||
}
|
||||
|
||||
final double currentTime = glfwGetTime();
|
||||
io.setDeltaTime(time > 0.0 ? (float) (currentTime - time) : 1.0f / 60.0f);
|
||||
|
||||
updateMousePosAndButtons(scaleX, scaleY);
|
||||
updateMouseCursor();
|
||||
updateGamepads();
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
|
||||
Reference in new issue
Block a user