From 24cd39ce0a7563a285705b3271e651573b6770fa Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Mon, 30 Mar 2026 23:17:24 +0300 Subject: [PATCH 01/15] reworking vr keyboard layouts (cherry picked from commit 58f4634df8cc30ec5141a76d11110e2f52a5b2b1) --- .../builtin/keyboard/KeyboardKey.java | 28 ++ .../builtin/keyboard/KeyboardLayout.java | 38 +++ .../builtin/keyboard/KeyboardLayoutId.java | 27 ++ .../builtin/keyboard/KeyboardLayouts.java | 271 +++++++++++++++ .../builtin/keyboard/VROverlayKeyboard.java | 43 ++- .../client/gui/screens/VRKeyboardScreen.java | 310 +++++++++--------- 6 files changed, 550 insertions(+), 167 deletions(-) create mode 100644 visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardKey.java create mode 100644 visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayout.java create mode 100644 visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java create mode 100644 visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardKey.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardKey.java new file mode 100644 index 00000000..136ebe6b --- /dev/null +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardKey.java @@ -0,0 +1,28 @@ +package org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard; + +import lombok.Getter; + +public class KeyboardKey { + @Getter + private final String label; + @Getter + private final String input; + @Getter + private final int fallbackKey; + @Getter + private final int fallbackModifiers; + + public KeyboardKey(String label, + String input, + int fallbackKey, + int fallbackModifiers) { + this.label = label; + this.input = input; + this.fallbackKey = fallbackKey; + this.fallbackModifiers = fallbackModifiers; + } + + public boolean hasFallback() { + return fallbackKey != -1; + } +} \ No newline at end of file diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayout.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayout.java new file mode 100644 index 00000000..2da45897 --- /dev/null +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayout.java @@ -0,0 +1,38 @@ +package org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +public class KeyboardLayout { + @Getter + private final KeyboardLayoutId id; + @Getter + private final String switchLabel; + private final KeyboardKey[][] normalLayer; + private final KeyboardKey[][] shiftLayer; + private final int maxColumns; + + public KeyboardLayout(@NotNull KeyboardLayoutId id, + @NotNull String switchLabel, + @NotNull KeyboardKey[][] normalLayer, + @NotNull KeyboardKey[][] shiftLayer) { + this.id = id; + this.switchLabel = switchLabel; + this.normalLayer = normalLayer; + this.shiftLayer = shiftLayer; + + int columns = 0; + for (KeyboardKey[] row : normalLayer) { + columns = Math.max(columns, row.length); + } + this.maxColumns = columns; + } + + public @NotNull KeyboardKey[][] getLayer(boolean shifted) { + return shifted ? shiftLayer : normalLayer; + } + + public int getMaxColumns() { + return maxColumns; + } +} \ No newline at end of file diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java new file mode 100644 index 00000000..a00d1913 --- /dev/null +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java @@ -0,0 +1,27 @@ +package org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +public enum KeyboardLayoutId { + EN_US("EN"), + RU("RU"), + UA("UA"), + DE("DE"), + FR("FR"), + ES_ES("ES"), + IT("IT"), + PT_PT("PT"); + + @Getter + private final String buttonLabel; + + KeyboardLayoutId(@NotNull String buttonLabel) { + this.buttonLabel = buttonLabel; + } + + public @NotNull KeyboardLayoutId next() { + KeyboardLayoutId[] values = values(); + return values[(ordinal() + 1) % values.length]; + } +} \ No newline at end of file diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java new file mode 100644 index 00000000..0ca8074c --- /dev/null +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java @@ -0,0 +1,271 @@ +package org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard; + +import org.jetbrains.annotations.NotNull; +import org.lwjgl.glfw.GLFW; + +import java.util.EnumMap; +import java.util.Map; + +public final class KeyboardLayouts { + + private static final int[][] ROW_KEY_CODES = new int[][]{ + { + GLFW.GLFW_KEY_GRAVE_ACCENT, + GLFW.GLFW_KEY_1, + GLFW.GLFW_KEY_2, + GLFW.GLFW_KEY_3, + GLFW.GLFW_KEY_4, + GLFW.GLFW_KEY_5, + GLFW.GLFW_KEY_6, + GLFW.GLFW_KEY_7, + GLFW.GLFW_KEY_8, + GLFW.GLFW_KEY_9, + GLFW.GLFW_KEY_0, + GLFW.GLFW_KEY_MINUS, + GLFW.GLFW_KEY_EQUAL + }, + { + GLFW.GLFW_KEY_Q, + GLFW.GLFW_KEY_W, + GLFW.GLFW_KEY_E, + GLFW.GLFW_KEY_R, + GLFW.GLFW_KEY_T, + GLFW.GLFW_KEY_Y, + GLFW.GLFW_KEY_U, + GLFW.GLFW_KEY_I, + GLFW.GLFW_KEY_O, + GLFW.GLFW_KEY_P, + GLFW.GLFW_KEY_LEFT_BRACKET, + GLFW.GLFW_KEY_RIGHT_BRACKET, + GLFW.GLFW_KEY_BACKSLASH + }, + { + GLFW.GLFW_KEY_A, + GLFW.GLFW_KEY_S, + GLFW.GLFW_KEY_D, + GLFW.GLFW_KEY_F, + GLFW.GLFW_KEY_G, + GLFW.GLFW_KEY_H, + GLFW.GLFW_KEY_J, + GLFW.GLFW_KEY_K, + GLFW.GLFW_KEY_L, + GLFW.GLFW_KEY_SEMICOLON, + GLFW.GLFW_KEY_APOSTROPHE + }, + { + GLFW.GLFW_KEY_Z, + GLFW.GLFW_KEY_X, + GLFW.GLFW_KEY_C, + GLFW.GLFW_KEY_V, + GLFW.GLFW_KEY_B, + GLFW.GLFW_KEY_N, + GLFW.GLFW_KEY_M, + GLFW.GLFW_KEY_COMMA, + GLFW.GLFW_KEY_PERIOD, + GLFW.GLFW_KEY_SLASH + } + }; + + private static final Map LAYOUTS = new EnumMap<>( + KeyboardLayoutId.class + ); + + static { + register(build( + KeyboardLayoutId.EN_US, + new String[]{ + "`1234567890-=", + "qwertyuiop[]\\", + "asdfghjkl;'", + "zxcvbnm,./" + }, + new String[]{ + "~!@#$%^&*()_+", + "QWERTYUIOP{}|", + "ASDFGHJKL:\"", + "ZXCVBNM<>?" + } + )); + register(build( + KeyboardLayoutId.RU, + new String[]{ + "ё1234567890-=", + "йцукенгшщзхъ\\", + "фывапролджэ", + "ячсмитьбю." + }, + new String[]{ + "Ё!\"№;%:?*()_+", + "ЙЦУКЕНГШЩЗХЪ/", + "ФЫВАПРОЛДЖЭ", + "ЯЧСМИТЬБЮ," + } + )); + register(build( + KeyboardLayoutId.UA, + new String[]{ + "'1234567890-=", + "йцукенгшщзхїґ", + "фівапролджє", + "ячсмитьбю." + }, + new String[]{ + "₴!\"№;%:?*()_+", + "ЙЦУКЕНГШЩЗХЇҐ", + "ФІВАПРОЛДЖЄ", + "ЯЧСМИТЬБЮ," + } + )); + register(build( + KeyboardLayoutId.DE, + new String[]{ + "^1234567890ß´", + "qwertzuiopü+#", + "asdfghjklöä", + "yxcvbnm,.-" + }, + new String[]{ + "°!\"§$%&/()=?`", + "QWERTZUIOPÜ*'", + "ASDFGHJKLÖÄ", + "YXCVBNM;:_" + } + )); + register(build( + KeyboardLayoutId.FR, + new String[]{ + "²&é\"'(-è_çà)=", + "azertyuiop^$*", + "qsdfghjklmù", + "wxcvbn,;:!" + }, + new String[]{ + "³1234567890°+", + "AZERTYUIOP¨£µ", + "QSDFGHJKLM%", + "WXCVBN?./§" + } + )); + register(build( + KeyboardLayoutId.ES_ES, + new String[]{ + "º1234567890'¡", + "qwertyuiop`+ç", + "asdfghjklñ´", + "zxcvbnm,.-" + }, + new String[]{ + "ª!\"·$%&/()=?¿", + "QWERTYUIOP^*Ç", + "ASDFGHJKLѨ", + "ZXCVBNM;:_" + } + )); + register(build( + KeyboardLayoutId.IT, + new String[]{ + "\\1234567890'ì", + "qwertyuiopè+ù", + "asdfghjklòà", + "zxcvbnm,.-" + }, + new String[]{ + "|!\"£$%&/()=?^", + "QWERTYUIOPé*§", + "ASDFGHJKLç°", + "ZXCVBNM;:_" + } + )); + register(build( + KeyboardLayoutId.PT_PT, + new String[]{ + "\\1234567890'«", + "qwertyuiop+´~", + "asdfghjklçº", + "zxcvbnm,.-" + }, + new String[]{ + "|!\"#$%&/()=?»", + "QWERTYUIOP*`^", + "ASDFGHJKLǪ", + "ZXCVBNM;:_" + } + )); + } + + private KeyboardLayouts() { + } + + public static @NotNull KeyboardLayout get(@NotNull KeyboardLayoutId id) { + KeyboardLayout layout = LAYOUTS.get(id); + if (layout == null) { + throw new IllegalArgumentException("Unknown keyboard layout: " + id); + } + return layout; + } + + public static @NotNull KeyboardLayout getDefault() { + return get(KeyboardLayoutId.EN_US); + } + + private static void register(@NotNull KeyboardLayout layout) { + LAYOUTS.put(layout.getId(), layout); + } + + private static @NotNull KeyboardLayout build(@NotNull KeyboardLayoutId id, + @NotNull String[] normalRows, + @NotNull String[] shiftRows) { + if (normalRows.length != ROW_KEY_CODES.length + || shiftRows.length != ROW_KEY_CODES.length) { + throw new IllegalArgumentException( + "Keyboard layout " + id + " must define " + ROW_KEY_CODES.length + " rows" + ); + } + + KeyboardKey[][] normalLayer = new KeyboardKey[normalRows.length][]; + KeyboardKey[][] shiftLayer = new KeyboardKey[shiftRows.length][]; + + for (int row = 0; row < normalRows.length; row++) { + normalLayer[row] = buildRow(id, row, normalRows[row], 0); + shiftLayer[row] = buildRow(id, row, shiftRows[row], GLFW.GLFW_MOD_SHIFT); + } + + return new KeyboardLayout(id, id.getButtonLabel(), normalLayer, shiftLayer); + } + + private static @NotNull KeyboardKey[] buildRow(@NotNull KeyboardLayoutId id, + int rowIndex, + @NotNull String rowContent, + int fallbackModifiers) { + int[] rowKeyCodes = ROW_KEY_CODES[rowIndex]; + String[] symbols = splitSymbols(rowContent); + if (symbols.length != rowKeyCodes.length) { + throw new IllegalArgumentException( + "Keyboard layout " + id + + " row " + rowIndex + + " expected " + rowKeyCodes.length + + " symbols but got " + symbols.length + ); + } + + KeyboardKey[] result = new KeyboardKey[symbols.length]; + for (int i = 0; i < symbols.length; i++) { + result[i] = new KeyboardKey( + symbols[i], + symbols[i], + rowKeyCodes[i], + fallbackModifiers + ); + } + return result; + } + + private static @NotNull String[] splitSymbols(@NotNull String rowContent) { + int[] codePoints = rowContent.codePoints().toArray(); + String[] symbols = new String[codePoints.length]; + for (int i = 0; i < codePoints.length; i++) { + symbols[i] = new String(Character.toChars(codePoints[i])); + } + return symbols; + } +} diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java index 7e94a41d..f3261247 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java @@ -1,30 +1,28 @@ package org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard; - import lombok.Getter; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector3fc; import org.vmstudio.visor.api.VisorAPI; import org.vmstudio.visor.api.client.ClientFeature; -import org.vmstudio.visor.api.client.player.pose.PoseAnchor; -import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; import org.vmstudio.visor.api.client.events.AllowClientFeatureVREvent; import org.vmstudio.visor.api.client.gui.VRKeyboardAccessor; import org.vmstudio.visor.api.client.gui.overlays.VROverlayHelper; import org.vmstudio.visor.api.client.gui.overlays.framework.screen.VROverlayScreenInScreen; -import org.vmstudio.visor.api.common.addon.component.ComponentPriority; +import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; +import org.vmstudio.visor.api.client.player.pose.PoseAnchor; import org.vmstudio.visor.api.common.addon.VisorAddon; +import org.vmstudio.visor.api.common.addon.component.ComponentPriority; import org.vmstudio.visor.api.common.eventbus.listener.VREventHandler; import org.vmstudio.visor.api.common.eventbus.listener.VREventListener; import org.vmstudio.visor.core.client.ClientContext; import org.vmstudio.visor.core.client.gui.screens.VRKeyboardScreen; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.Component; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector3fc; - public class VROverlayKeyboard extends VROverlayScreenInScreen implements VRKeyboardAccessor, VREventListener { @@ -39,6 +37,9 @@ public class VROverlayKeyboard extends VROverlayScreenInScreen @Getter private boolean shiftPressed = false; + @Getter + private KeyboardLayoutId activeLayoutId = KeyboardLayoutId.EN_US; + @Getter @Nullable private Screen attachedTo; @@ -113,7 +114,6 @@ public boolean updateVisibility() { return shown; } - @Override public void onUpdatePose(float partialTicks) { VROverlayHelper.applyRelativePose( @@ -124,8 +124,6 @@ public void onUpdatePose(float partialTicks) { ); } - - @Override public boolean supportsTwoCursors() { return true; @@ -139,6 +137,7 @@ public void setVisible(boolean flag, if (shown) { orient(attachedTo); shiftPressed = false; + activeLayoutId = KeyboardLayoutId.EN_US; initAgain = true; } else { getScreen().clearPress(); @@ -154,6 +153,17 @@ public void setShiftPressed(boolean shift) { } } + public void cycleLayout() { + setActiveLayoutId(activeLayoutId.next()); + } + + public void setActiveLayoutId(@NotNull KeyboardLayoutId activeLayoutId) { + if (this.activeLayoutId != activeLayoutId) { + this.activeLayoutId = activeLayoutId; + this.initAgain = true; + } + } + private void orient(@Nullable Screen attachedTo) { if (!shown) { this.attachedTo = null; @@ -187,5 +197,4 @@ private void orient(@Nullable Screen attachedTo) { public @NotNull Component getDescription() { return Component.translatable("visor.overlay.%s.description".formatted(getId())); } - } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java index 5b64d5e2..debbd5d2 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java @@ -2,24 +2,29 @@ import lombok.Getter; import lombok.Setter; -import org.vmstudio.visor.api.client.input.InputHelper; -import org.vmstudio.visor.core.client.ClientContext; -import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardButton; -import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.VROverlayKeyboard; -import org.vmstudio.visor.core.client.settings.VRClientSettings; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import org.lwjgl.glfw.GLFW; - +import org.vmstudio.visor.api.client.input.InputHelper; +import org.vmstudio.visor.core.client.ClientContext; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardButton; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardKey; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardLayout; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardLayouts; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.VROverlayKeyboard; public class VRKeyboardScreen extends Screen { - @Getter @Setter + @Getter + @Setter private VROverlayKeyboard overlayKeyboard; - @Getter @Setter + @Getter + @Setter private Runnable pressedTask; - @Getter @Setter + @Getter + @Setter private int pressTick; public VRKeyboardScreen(Component component) { @@ -28,63 +33,65 @@ public VRKeyboardScreen(Component component) { @Override public void init() { - String keys = VRClientSettings.getKeyboardKeys(); - String keysShift = VRClientSettings.getKeyboardKeysShift(); this.clearWidgets(); - if (overlayKeyboard.isShiftPressed()) { - keys = keysShift; - } + int gridStart = 32; + int keyGap = 2; + int keyWidth = 25; + int keyHeight = 20; + int smallButtonWidth = 30; + int sideButtonWidth = 35; + int shiftButtonWidth = 50; + int languageButtonWidth = 38; + int spaceX; - int keysPerRow = 13; - int rows; - int yPos = 32; - int l = 2; - int i1 = 25; - double preRows = (double) keys.length() / (double) keysPerRow; + KeyboardLayout layout = KeyboardLayouts.get(overlayKeyboard.getActiveLayoutId()); + KeyboardKey[][] rows = layout.getLayer(overlayKeyboard.isShiftPressed()); + int maxColumns = layout.getMaxColumns(); + int gridRightX = gridStart + maxColumns * (keyWidth + keyGap); - if (Math.floor(preRows) == preRows) { - rows = (int) preRows; - } else { - rows = (int) (preRows + 1.0D); - } + for (int row = 0; row < rows.length; ++row) { + KeyboardKey[] rowKeys = rows[row]; + int rowStartX = gridStart + ((maxColumns - rowKeys.length) * (keyWidth + keyGap)) / 2; + int rowY = gridStart + row * (keyHeight + keyGap); - for (int row = 0; row < rows; ++row) { - for (int column = 0; column < keysPerRow; ++column) { - int index = row * keysPerRow + column; - char keyChar = ' '; - - if (index < keys.length()) { - keyChar = keys.charAt(index); - } - - String label = String.valueOf(keyChar); + for (int column = 0; column < rowKeys.length; ++column) { + KeyboardKey key = rowKeys[column]; KeyboardButton button = new KeyboardButton.Builder( this, - Component.literal(label), (p) -> { - - InputHelper.typeChars(label); - - }).size(i1, 20) - .pos(yPos + column * (i1 + l), yPos + row * (20 + l)) + Component.literal(key.getLabel()), + (p) -> pressKeyboardKey(key) + ).size(keyWidth, keyHeight) + .pos(rowStartX + column * (keyWidth + keyGap), rowY) .build(); this.addRenderableWidget(button); } } + int bottomY = gridStart + rows.length * (keyHeight + keyGap); + spaceX = gridStart + ((maxColumns - 5) / 2) * (keyWidth + keyGap); + //SHIFT this.addRenderableWidget( new KeyboardButton.Builder(this, - Component.literal(overlayKeyboard.isShiftPressed() - ? "SHIFT" - : "Shift"), - (p) -> - { - overlayKeyboard - .setShiftPressed(!overlayKeyboard.isShiftPressed()); - }) - .size(overlayKeyboard.isShiftPressed() ? 32 : 30, 20) - .pos(0, yPos + 3 * (20 + l)) + Component.literal( + overlayKeyboard.isShiftPressed() + ? "SHIFT" + : "Shift" + ), + (p) -> overlayKeyboard.setShiftPressed(!overlayKeyboard.isShiftPressed())) + .size(overlayKeyboard.isShiftPressed() ? (shiftButtonWidth + 2) : shiftButtonWidth, keyHeight) + .pos(0, gridStart + (rows.length - 1) * (keyHeight + keyGap)) + .usePressTask(false) + .build() + ); + //LANGUAGE + this.addRenderableWidget( + new KeyboardButton.Builder(this, + Component.literal(layout.getSwitchLabel()), + (p) -> overlayKeyboard.cycleLayout()) + .size(languageButtonWidth, keyHeight) + .pos(3 * (sideButtonWidth + keyGap) + gridStart, gridStart - (keyHeight + keyGap)) .usePressTask(false) .build() ); @@ -92,38 +99,27 @@ public void init() { this.addRenderableWidget( new KeyboardButton.Builder(this, Component.literal(" "), - (p) -> - { - InputHelper.typeChars(" "); - }) - .size(5 * (i1 + l), 20) - .pos(yPos + 4 * (i1 + l), yPos + rows * (20 + l)) + (p) -> pressSpace()) + .size(5 * (keyWidth + keyGap), keyHeight) + .pos(spaceX, bottomY) .build() ); //BACKSPACE this.addRenderableWidget( new KeyboardButton.Builder(this, Component.literal("BKSP"), - (p) -> - { - InputHelper.pressKey(GLFW.GLFW_KEY_BACKSPACE); - InputHelper.releaseKey(GLFW.GLFW_KEY_BACKSPACE); - }) - .size(35, 20) - .pos(keysPerRow * (i1 + l) + yPos, yPos) + (p) -> pressKeyAction(GLFW.GLFW_KEY_BACKSPACE)) + .size(sideButtonWidth, keyHeight) + .pos(gridRightX, gridStart) .build() ); //ENTER this.addRenderableWidget( new KeyboardButton.Builder(this, Component.literal("ENTER"), - (p) -> - { - InputHelper.pressKey(GLFW.GLFW_KEY_ENTER); - InputHelper.releaseKey(GLFW.GLFW_KEY_ENTER); - }) - .size(35, 20) - .pos(keysPerRow * (i1 + l) + yPos, yPos + 2 * (20 + l)) + (p) -> pressKeyAction(GLFW.GLFW_KEY_ENTER)) + .size(sideButtonWidth, keyHeight) + .pos(gridRightX, gridStart + 2 * (keyHeight + keyGap)) .usePressTask(false) .build() ); @@ -131,118 +127,70 @@ public void init() { this.addRenderableWidget( new KeyboardButton.Builder(this, Component.literal("TAB"), - (p) -> - { - InputHelper.pressKey(GLFW.GLFW_KEY_TAB); - InputHelper.releaseKey(GLFW.GLFW_KEY_TAB); - }) - .size(30, 20) - .pos(0, yPos + 20 + l) + (p) -> pressKeyAction(GLFW.GLFW_KEY_TAB)) + .size(smallButtonWidth, keyHeight) + .pos(0, gridStart + keyHeight + keyGap) .usePressTask(false) .build() ); - //CLOSE this.addRenderableWidget( new KeyboardButton.Builder(this, Component.literal("§cx"), (p) -> { - var keyboardAccessor = ClientContext.overlayManager - .getKeyboardAccessor(); + var keyboardAccessor = ClientContext.overlayManager.getKeyboardAccessor(); keyboardAccessor.setVisible(false); }) - .size(30, 20) - .pos(0, yPos + -1 * (20 + l)) + .size(smallButtonWidth, keyHeight) + .pos(0, gridStart - (keyHeight + keyGap)) .usePressTask(false) .build() ); - //ESCAPE this.addRenderableWidget( new KeyboardButton.Builder(this, Component.literal("ESC"), - (p) -> - { - InputHelper.pressKey(GLFW.GLFW_KEY_ESCAPE); - InputHelper.releaseKey(GLFW.GLFW_KEY_ESCAPE); - }) - .size(30, 20) - .pos(0, yPos) + (p) -> pressKeyAction(GLFW.GLFW_KEY_ESCAPE)) + .size(smallButtonWidth, keyHeight) + .pos(0, gridStart) .usePressTask(false) .build() ); //ARROW UP this.addRenderableWidget( new KeyboardButton.Builder(this, - Component.literal("\u2191"), - (p) -> - { - if(overlayKeyboard.isShiftPressed()){ - InputHelper.pressKey(GLFW.GLFW_KEY_LEFT_SHIFT); - } - - InputHelper.pressKey(GLFW.GLFW_KEY_UP); - InputHelper.releaseKey(GLFW.GLFW_KEY_UP); - - InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_SHIFT); - }) - .size(i1, 20) - .pos((keysPerRow - 1) * (i1 + l) + yPos, yPos + rows * (20 + l)) + Component.literal("↑"), + (p) -> pressNavigationKey(GLFW.GLFW_KEY_UP)) + .size(keyWidth, keyHeight) + .pos((maxColumns - 1) * (keyWidth + keyGap) + gridStart, bottomY) .build() ); //ARROW DOWN this.addRenderableWidget( new KeyboardButton.Builder(this, - Component.literal("\u2193"), - (p) -> - { - if(overlayKeyboard.isShiftPressed()){ - InputHelper.pressKey(GLFW.GLFW_KEY_LEFT_SHIFT); - } - InputHelper.pressKey(GLFW.GLFW_KEY_DOWN); - InputHelper.releaseKey(GLFW.GLFW_KEY_DOWN); - - InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_SHIFT); - }) - .size(i1, 20) - .pos((keysPerRow - 1) * (i1 + l) + yPos, yPos + (rows + 1) * (20 + l)) + Component.literal("↓"), + (p) -> pressNavigationKey(GLFW.GLFW_KEY_DOWN)) + .size(keyWidth, keyHeight) + .pos((maxColumns - 1) * (keyWidth + keyGap) + gridStart, bottomY + keyHeight + keyGap) .build() ); //ARROW LEFT this.addRenderableWidget( new KeyboardButton.Builder(this, - Component.literal("\u2190"), - (p) -> - { - if(overlayKeyboard.isShiftPressed()){ - InputHelper.pressKey(GLFW.GLFW_KEY_LEFT_SHIFT); - } - InputHelper.pressKey(GLFW.GLFW_KEY_LEFT); - InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT); - - InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_SHIFT); - }) - .size(i1, 20) - .pos((keysPerRow - 2) * (i1 + l) + yPos, yPos + (rows + 1) * (20 + l)) + Component.literal("←"), + (p) -> pressNavigationKey(GLFW.GLFW_KEY_LEFT)) + .size(keyWidth, keyHeight) + .pos((maxColumns - 2) * (keyWidth + keyGap) + gridStart, bottomY + keyHeight + keyGap) .build() ); //ARROW RIGHT this.addRenderableWidget( new KeyboardButton.Builder(this, - Component.literal("\u2192"), - (p) -> - { - if(overlayKeyboard.isShiftPressed()){ - InputHelper.pressKey(GLFW.GLFW_KEY_LEFT_SHIFT); - } - InputHelper.pressKey(GLFW.GLFW_KEY_RIGHT); - InputHelper.releaseKey(GLFW.GLFW_KEY_RIGHT); - - InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_SHIFT); - }) - .size(i1, 20) - .pos(keysPerRow * (i1 + l) + yPos, yPos + (rows + 1) * (20 + l)) + Component.literal("→"), + (p) -> pressNavigationKey(GLFW.GLFW_KEY_RIGHT)) + .size(keyWidth, keyHeight) + .pos(maxColumns * (keyWidth + keyGap) + gridStart, bottomY + keyHeight + keyGap) .build() ); //CUT @@ -256,8 +204,8 @@ public void init() { InputHelper.releaseKey(GLFW.GLFW_KEY_X); InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_CONTROL); }) - .size(35, 20) - .pos(yPos, yPos + -1 * (20 + l)) + .size(sideButtonWidth, keyHeight) + .pos(gridStart, gridStart - (keyHeight + keyGap)) .usePressTask(false) .build() ); @@ -272,8 +220,8 @@ public void init() { InputHelper.releaseKey(GLFW.GLFW_KEY_C); InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_CONTROL); }) - .size(35, 20) - .pos(35 + l + yPos, yPos + -1 * (20 + l)) + .size(sideButtonWidth, keyHeight) + .pos(sideButtonWidth + keyGap + gridStart, gridStart - (keyHeight + keyGap)) .usePressTask(false) .build() ); @@ -288,13 +236,75 @@ public void init() { InputHelper.releaseKey(GLFW.GLFW_KEY_V); InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_CONTROL); }) - .size(35, 20) - .pos(2 * (35 + l) + yPos, yPos + -1 * (20 + l)) + .size(sideButtonWidth, keyHeight) + .pos(2 * (sideButtonWidth + keyGap) + gridStart, gridStart - (keyHeight + keyGap)) .usePressTask(false) .build() ); } + private void pressKeyboardKey(KeyboardKey key) { + if (canTypeText()) { + InputHelper.typeChars(key.getInput()); + return; + } + + pressFallbackKey(key); + } + + private void pressFallbackKey(KeyboardKey key) { + if (!key.hasFallback()) { + return; + } + + pressKeyAction(key.getFallbackKey(), key.getFallbackModifiers()); + } + + private void pressSpace() { + if (canTypeText()) { + InputHelper.typeChars(" "); + return; + } + + pressKeyAction(GLFW.GLFW_KEY_SPACE); + } + + private void pressNavigationKey(int key) { + int modifiers = overlayKeyboard.isShiftPressed() + ? GLFW.GLFW_MOD_SHIFT + : 0; + pressKeyAction(key, modifiers); + } + + private void pressKeyAction(int key) { + pressKeyAction(key, 0); + } + + private void pressKeyAction(int key, + int temporaryModifiers) { + pressModifiers(temporaryModifiers); + InputHelper.pressKey(key, temporaryModifiers); + InputHelper.releaseKey(key, temporaryModifiers); + releaseModifiers(temporaryModifiers); + } + + private boolean canTypeText() { + return overlayKeyboard.getAttachedTo() != null + || Minecraft.getInstance().screen != null; + } + + private void pressModifiers(int modifiers) { + if ((modifiers & GLFW.GLFW_MOD_SHIFT) != 0) { + InputHelper.pressKey(GLFW.GLFW_KEY_LEFT_SHIFT); + } + } + + private void releaseModifiers(int modifiers) { + if ((modifiers & GLFW.GLFW_MOD_SHIFT) != 0) { + InputHelper.releaseKey(GLFW.GLFW_KEY_LEFT_SHIFT); + } + } + @Override public void renderBackground(GuiGraphics guiGraphics) { From 513905914ccbe1ccd04be542d7502546d58ca561 Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Tue, 31 Mar 2026 22:51:01 +0300 Subject: [PATCH 02/15] redesigned game menu screen (cherry picked from commit bc754c7e27ec7b2ba621e6c268f64460ddcbe112) --- .../client/gui/screens/GameMenuScreen.java | 298 ++++++++++++++---- 1 file changed, 230 insertions(+), 68 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java index 2d5a7d1c..f1727f7d 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java @@ -1,23 +1,48 @@ package org.vmstudio.visor.core.client.gui.screens; -import org.vmstudio.visor.core.client.ClientContext; -import org.vmstudio.visor.core.client.settings.VRClientSettings; -import org.vmstudio.visor.core.client.tasks.types.TaskHotBar; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.layouts.FrameLayout; -import net.minecraft.client.gui.layouts.GridLayout; import net.minecraft.client.gui.screens.ChatScreen; import net.minecraft.client.gui.screens.PauseScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.network.chat.Component; +import org.vmstudio.visor.core.client.ClientContext; +import org.vmstudio.visor.core.client.settings.VRClientSettings; +import org.vmstudio.visor.core.client.tasks.types.TaskHotBar; +import java.util.ArrayList; +import java.util.List; public class GameMenuScreen extends Screen { - private static final Component OPEN_CHAT = Component.literal("Chat"); - private static final Component OPEN_INVENTORY = Component.literal("Inventory"); - private static final Component OPEN_PAUSE_MENU = Component.literal("Main Menu"); - private static final Component OPEN_KEYBOARD = Component.literal("Keyboard"); + private static final int COLUMN_W = 204; + private static final int BTN_H = 20; + private static final int BTN_HALF = (COLUMN_W - 4) / 2; + private static final int GAP = 4; + private static final int TAB_W = (COLUMN_W - 8) / 3; + + private static final int BTN_QUARTER = (COLUMN_W - 3 * GAP) / 4; + private static final int BTN_THIRD = (COLUMN_W - 2 * GAP) / 3; + + private static final int LABEL_H = 11; + private static final int SECTION_GAP = 6; + + private enum Tab{ + QUICK_ACTIONS(Component.literal("Quick Actions")), + WORLD_AND_MODES(Component.literal("World & Modes")), + TOOLS(Component.literal("F3 Tools")); + + Component label; + + Tab(Component l) { + this.label = l; + } + } + + private Tab currentTab = Tab.QUICK_ACTIONS; + + private final List sectionHeaderPos = new ArrayList<>(); // {x, y} + private final List sectionHeaderTexts = new ArrayList<>(); public GameMenuScreen() { super(Component.literal("Game Menu")); @@ -26,70 +51,207 @@ public GameMenuScreen() { @Override protected void init() { - create(); - //@TODO temporary. Get rid of it when player tick tasks - // start to reset on player world leave - TaskHotBar.getInstance().setResetData(true); - var keyboardAccessor = ClientContext.overlayManager - .getKeyboardAccessor(); - keyboardAccessor.setVisible(false); - } + TaskHotBar.setResetData(true); + ClientContext.overlayManager.getKeyboardAccessor().setVisible(false); + boolean hasPerms = this.minecraft.player != null && this.minecraft.player.hasPermissions(2); - private void create(){ - GridLayout gridLayout = new GridLayout(); - gridLayout.defaultCellSetting().padding(4, 4, 4, 0); - GridLayout.RowHelper rowHelper = gridLayout.createRowHelper(2); - - - //INVENTORY - rowHelper.addChild(Button.builder(OPEN_INVENTORY, (button) -> { - this.minecraft.setScreen(null); - this.minecraft.setScreen(new InventoryScreen( - this.minecraft.player) - ); - }).width(104).build(), - 2, gridLayout.newCellSettings().paddingTop(50)); - - //CHAT - rowHelper.addChild(new Button.Builder(OPEN_CHAT, (p) -> - { - this.minecraft.setScreen(null); - this.minecraft.setScreen(new ChatScreen("")); - }).width(104).build(), - 2, gridLayout.newCellSettings().paddingTop(20)); - //---CHAT END - - - //KEYBOARD - rowHelper.addChild(Button.builder(OPEN_KEYBOARD, (button) -> { - this.minecraft.setScreen(null); - var keyboardAccessor = ClientContext.overlayManager - .getKeyboardAccessor(); - keyboardAccessor.setVisible(true); - }).width(104).build(), - 2, gridLayout.newCellSettings().paddingTop(20) - ); + if (this.currentTab == Tab.WORLD_AND_MODES && !hasPerms) { + this.currentTab = Tab.QUICK_ACTIONS; + } - rowHelper.addChild(new Button.Builder(Component.translatable("visor.button.calibrate_height"), (p) -> - { - VRClientSettings.calibrateHeight(); - ClientContext.settingsManager.saveOptions(); - this.minecraft.setScreen(null); - }).width(104).build(), - 2, gridLayout.newCellSettings().paddingTop(20)); - - //PAUSE MENU - rowHelper.addChild(Button.builder(OPEN_PAUSE_MENU, (button) -> { - this.minecraft.setScreen(null); - this.minecraft.setScreen(new PauseScreen(true)); - }).width(104).build(), - 2, gridLayout.newCellSettings().paddingTop(20) + sectionHeaderPos.clear(); + sectionHeaderTexts.clear(); + + int cx = this.width / 2; + int startY = this.height / 2 - totalColumnHeight() / 2; + int y = startY + 9 + 16; + + int tx = cx - COLUMN_W / 2; + for (Tab tab : Tab.values()) { + final Tab t = tab; + Button btn = Button.builder(tab.label, b -> { + this.currentTab = t; + this.rebuildWidgets(); + }).pos(tx + tab.ordinal() * (TAB_W + 4), y).width(TAB_W).build(); + + if (t == Tab.WORLD_AND_MODES && !hasPerms) { + btn.active = false; + } else { + btn.active = (this.currentTab != tab); + } + addRenderableWidget(btn); + } + + // dont touch this please 🤞 + y += BTN_H + GAP + 2; + y = buildContent(cx, y); + y += 6; + + addRenderableWidget( + Button.builder(Component.literal("Back to Game"), b -> this.minecraft.setScreen(null)) + .pos(cx - COLUMN_W / 2, y).width(COLUMN_W).build() ); + } + + private int buildContent(int cx, int y) { + int left = cx - COLUMN_W / 2; + int right = left + BTN_HALF + GAP; + + switch (this.currentTab) { + case QUICK_ACTIONS -> { + addRenderableWidget(makeHalfBtn("Inventory", left, y, + b -> this.minecraft.setScreen(new InventoryScreen(this.minecraft.player)))); + addRenderableWidget(makeHalfBtn("Chat", right, y, + b -> this.minecraft.setScreen(new ChatScreen("")))); + y += BTN_H + GAP; + + addRenderableWidget(makeHalfBtn("Keyboard", left, y, b -> + ClientContext.overlayManager.getKeyboardAccessor().setVisible(true))); + addRenderableWidget(makeHalfBtn("Calibrate Height", right, y, b -> { + VRClientSettings.calibrateHeight(); + ClientContext.settingsManager.saveOptions(); + })); + y += BTN_H + GAP; + + addRenderableWidget(makeFullBtn("Vanilla Pause Menu", left, y, + b -> this.minecraft.setScreen(new PauseScreen(true)))); + y += BTN_H + GAP; + } + + case WORLD_AND_MODES -> { + registerSection(left, y, "GAME MODE"); + y += LABEL_H; + + addRenderableWidget(makeHalfBtn("Survival", left, y, b -> sendCommand("gamemode survival"))); + addRenderableWidget(makeHalfBtn("Creative", right, y, b -> sendCommand("gamemode creative"))); + y += BTN_H + GAP; + + addRenderableWidget(makeHalfBtn("Spectator", left, y, b -> sendCommand("gamemode spectator"))); + addRenderableWidget(makeHalfBtn("Adventure", right, y, b -> sendCommand("gamemode adventure"))); + y += BTN_H + GAP; + + y += SECTION_GAP; + + registerSection(left, y, "TIME"); + y += LABEL_H; + int[] timeTicks = {0, 6000, 12000, 18000}; + String[] timeLabels = {"Dawn", "Noon", "Dusk", "Night"}; + for (int i = 0; i < 4; i++) { + final int tick = timeTicks[i]; + addRenderableWidget(Button.builder(Component.literal(timeLabels[i]), + b -> sendCommand("time set " + tick)) + .pos(left + i * (BTN_QUARTER + GAP), y) + .width(BTN_QUARTER) + .build()); + } + y += BTN_H + GAP; - gridLayout.arrangeElements(); - FrameLayout.alignInRectangle(gridLayout, 0, 0, this.width, this.height, 0.5F, 0.25F); - gridLayout.visitWidgets(this::addRenderableWidget); + y += SECTION_GAP; + + registerSection(left, y, "WEATHER"); + y += LABEL_H; + + String[] weatherLabels = {"Clear", "Rain", "Thunder"}; + String[] weatherCmds = {"weather clear", "weather rain", "weather thunder"}; + for (int i = 0; i < 3; i++) { + final String cmd = weatherCmds[i]; + addRenderableWidget(Button.builder(Component.literal(weatherLabels[i]), b -> sendCommand(cmd)) + .pos(left + i * (BTN_THIRD + GAP), y) + .width(BTN_THIRD) + .build()); + } + y += BTN_H + GAP; + } + + case TOOLS -> { + addRenderableWidget(makeHalfBtn("Hitboxes", left, y, b -> { + boolean cur = this.minecraft.getEntityRenderDispatcher().shouldRenderHitBoxes(); + this.minecraft.getEntityRenderDispatcher().setRenderHitBoxes(!cur); + })); + addRenderableWidget(makeHalfBtn("Chunk Borders", right, y, + b -> this.minecraft.debugRenderer.switchRenderChunkborder())); + y += BTN_H + GAP; + + addRenderableWidget(makeHalfBtn("Reload Chunks", left, y, + b -> this.minecraft.levelRenderer.allChanged())); + addRenderableWidget(makeHalfBtn("Clear Chat", right, y, + b -> this.minecraft.gui.getChat().clearMessages(false))); + y += BTN_H + GAP; + } + } + + return y; + } + + @Override + public void render(GuiGraphics gfx, int mouseX, int mouseY, float delta) { + super.renderBackground(gfx); + + int cx = this.width / 2; + int startY = this.height / 2 - totalColumnHeight() / 2; + + gfx.drawCenteredString(this.font, Component.literal("Game Menu"), cx, startY, 0xFFFFFFFF); + + int dividerColor = 0xFF555555; + + int divY = startY + 9 + 10; + gfx.fill(cx - COLUMN_W / 2, divY, cx + COLUMN_W / 2, divY + 1, dividerColor); + + int tabStripY = startY + 9 + 16; + int accentX = cx - COLUMN_W / 2 + currentTab.ordinal() * (TAB_W + 4); + gfx.fill(accentX, tabStripY - 2, accentX + TAB_W, tabStripY - 1, 0xFF55FF55); + + if (this.currentTab == Tab.WORLD_AND_MODES) { + for (int i = 0; i < sectionHeaderPos.size(); i++) { + int sx = sectionHeaderPos.get(i)[0]; + int sy = sectionHeaderPos.get(i)[1]; + + int lblW = this.font.width(sectionHeaderTexts.get(i)); + int lineY = sy + 4; + + gfx.fill(sx, lineY, sx + 18, lineY + 1, dividerColor); + gfx.fill(sx + 22 + lblW, lineY, sx + COLUMN_W, lineY + 1, dividerColor); + + gfx.drawString(this.font, sectionHeaderTexts.get(i), sx + 20, sy, 0xFF88FF88, false); + } + } + + super.render(gfx, mouseX, mouseY, delta); + } + + private int totalColumnHeight() { + int contentH = switch (this.currentTab) { + case QUICK_ACTIONS -> 3 * (BTN_H + GAP); + case TOOLS -> 2 * (BTN_H + GAP); + case WORLD_AND_MODES -> 3 * LABEL_H + 4 * (BTN_H + GAP) + 2 * SECTION_GAP; + }; + return 9 + 16 + BTN_H + 6 + contentH + 6 + BTN_H; + } + + private void registerSection(int x, int y, String text) { + sectionHeaderPos.add(new int[]{x, y}); + sectionHeaderTexts.add(text); } + private Button makeHalfBtn(String label, int x, int y, Button.OnPress action) { + return Button.builder(Component.literal(label), action) + .pos(x, y).width(BTN_HALF).build(); + } + + private Button makeFullBtn(String label, int x, int y, Button.OnPress action) { + return Button.builder(Component.literal(label), action) + .pos(x, y).width(COLUMN_W).build(); + } + + private void sendCommand(String command) { + if (this.minecraft.player != null) { + this.minecraft.player.connection.sendCommand(command); + } + } + + @Override + public boolean isPauseScreen() { + return false; + } } From eeb48ee9e482dad2e6f2004ed759038700166f84 Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Sat, 4 Apr 2026 00:47:50 +0300 Subject: [PATCH 03/15] redesigned chat (cherry picked from commit 9c15493b10374f2feb5b6d4f15912334ebb951c4) --- .../gui/overlays/templates/VROverlayChat.java | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/templates/VROverlayChat.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/templates/VROverlayChat.java index 63d08577..b06efe80 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/templates/VROverlayChat.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/templates/VROverlayChat.java @@ -7,9 +7,7 @@ import org.vmstudio.visor.api.client.gui.overlays.options.OverlayOptionGroup; import org.vmstudio.visor.api.client.gui.overlays.options.types.OverlayOptionsMisc; import org.vmstudio.visor.api.client.gui.overlays.options.types.OverlayOptionsPose; - import org.vmstudio.visor.api.client.gui.overlays.framework.template.VROverlayTemplateScreen; -import org.vmstudio.visor.api.common.HandType; import org.vmstudio.visor.api.common.addon.VisorAddon; import org.vmstudio.visor.core.client.ClientContext; import net.minecraft.client.gui.GuiGraphics; @@ -18,7 +16,6 @@ import java.util.List; - @RegisterVROverlayTemplate( id = VROverlayChat.ID, name = VROverlayChat.NAME, @@ -51,11 +48,6 @@ public boolean updateVisibility() { if(minecraft.level == null) return false; if(minecraft.isPaused() || ClientContext.overlayManager.getKeyboardAccessor().isVisible()) return false; - if (!ClientContext.rawPoseHandler.getControllerData(HandType.OFFHAND) - .isTracking()) { - return false; - } - return !minecraft.gui.getChat().trimmedMessages.isEmpty() && minecraft.options.chatVisibility().get() != ChatVisiblity.HIDDEN; @@ -69,7 +61,7 @@ public boolean supportsCursor() { @Override public boolean isHudLayer() { - return false; + return true; } @Override @@ -87,23 +79,22 @@ public boolean isHudLayer() { it.setTickPose(true); it.setAimedRotation(false); - it.setPositionAnchor(PoseAnchor.OFFHAND); + it.setPositionAnchor(PoseAnchor.HMD); it.setPositionOffset( - -0.15f, - 0.06f, - -0.13f + 0.06f + 0.0f, + 0.0f, + -2.5f ); - it.setRotationAnchor(PoseAnchor.OFFHAND); + it.setRotationAnchor(PoseAnchor.HMD); it.setRotationOffset( - (float) (-Math.PI / 2), - (float) (Math.PI / 2), + 0, + 0, 0 ); - it.setScale(0.5f); + it.setScale(1.2f); } ) ); } - } From 981bb170948f3fe6e119c55ca12aaf3cfc4a5205 Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Wed, 8 Apr 2026 00:21:33 +0300 Subject: [PATCH 04/15] keyboard layout settings (cherry picked from commit 39e048c3168598338ec5f3340d3bc0226f88d162) --- .../builtin/keyboard/KeyboardLayoutId.java | 32 ++-- .../builtin/keyboard/KeyboardLayouts.java | 46 ++++++ .../builtin/keyboard/VROverlayKeyboard.java | 27 +++- .../client/gui/screens/VRKeyboardScreen.java | 18 +-- .../categories/VRSettingsControls.java | 8 + .../controls/VRSettingsKeyboardLayouts.java | 145 ++++++++++++++++++ .../client/settings/VRClientSettings.java | 21 +++ .../resources/assets/visor/lang/en_us.json | 3 + 8 files changed, 280 insertions(+), 20 deletions(-) create mode 100644 visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/controls/VRSettingsKeyboardLayouts.java diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java index a00d1913..1b75fdcf 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayoutId.java @@ -2,26 +2,40 @@ import lombok.Getter; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public enum KeyboardLayoutId { - EN_US("EN"), - RU("RU"), - UA("UA"), - DE("DE"), - FR("FR"), - ES_ES("ES"), - IT("IT"), - PT_PT("PT"); + EN_US("EN", "English (US)"), + RU("RU", "Russian"), + UA("UA", "Ukrainian"), + DE("DE", "German"), + FR("FR", "French"), + ES_ES("ES", "Spanish"), + IT("IT", "Italian"), + PT_PT("PT", "Portuguese"); @Getter private final String buttonLabel; + @Getter + private final String displayName; - KeyboardLayoutId(@NotNull String buttonLabel) { + KeyboardLayoutId(@NotNull String buttonLabel, + @NotNull String displayName) { this.buttonLabel = buttonLabel; + this.displayName = displayName; } public @NotNull KeyboardLayoutId next() { KeyboardLayoutId[] values = values(); return values[(ordinal() + 1) % values.length]; } + + public static @Nullable KeyboardLayoutId byName(@NotNull String rawValue) { + for (KeyboardLayoutId value : values()) { + if (value.name().equalsIgnoreCase(rawValue)) { + return value; + } + } + return null; + } } \ No newline at end of file diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java index 0ca8074c..b7237ead 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/KeyboardLayouts.java @@ -1,10 +1,15 @@ package org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; +import java.util.ArrayList; import java.util.EnumMap; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; +import java.util.StringJoiner; public final class KeyboardLayouts { @@ -208,6 +213,47 @@ private KeyboardLayouts() { return get(KeyboardLayoutId.EN_US); } + public static @NotNull List getSelectableLayouts() { + return List.of(KeyboardLayoutId.values()); + } + + public static @NotNull List getEnabled( + @Nullable String rawValue + ) { + LinkedHashSet result = new LinkedHashSet<>(); + if (rawValue != null && !rawValue.isBlank()) { + for (String part : rawValue.split(",")) { + String trimmed = part.trim(); + if (trimmed.isEmpty()) { + continue; + } + KeyboardLayoutId layoutId = KeyboardLayoutId.byName(trimmed); + if (layoutId != null) { + result.add(layoutId); + } + } + } + + return List.copyOf(result); + } + + public static @NotNull String serializeEnabled( + @NotNull Iterable layoutIds + ) { + LinkedHashSet normalized = new LinkedHashSet<>(); + for (KeyboardLayoutId layoutId : layoutIds) { + if (layoutId != null) { + normalized.add(layoutId); + } + } + + StringJoiner joiner = new StringJoiner(","); + for (KeyboardLayoutId layoutId : normalized) { + joiner.add(layoutId.name()); + } + return joiner.toString(); + } + private static void register(@NotNull KeyboardLayout layout) { LAYOUTS.put(layout.getId(), layout); } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java index f3261247..f095ea90 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java @@ -23,6 +23,9 @@ import org.vmstudio.visor.api.common.eventbus.listener.VREventListener; import org.vmstudio.visor.core.client.ClientContext; import org.vmstudio.visor.core.client.gui.screens.VRKeyboardScreen; +import org.vmstudio.visor.core.client.settings.VRClientSettings; + +import java.util.List; public class VROverlayKeyboard extends VROverlayScreenInScreen implements VRKeyboardAccessor, VREventListener { @@ -137,7 +140,7 @@ public void setVisible(boolean flag, if (shown) { orient(attachedTo); shiftPressed = false; - activeLayoutId = KeyboardLayoutId.EN_US; + activeLayoutId = getEnabledLayoutIds().get(0); initAgain = true; } else { getScreen().clearPress(); @@ -154,7 +157,15 @@ public void setShiftPressed(boolean shift) { } public void cycleLayout() { - setActiveLayoutId(activeLayoutId.next()); + List enabledLayouts = getEnabledLayoutIds(); + int currentIndex = enabledLayouts.indexOf(activeLayoutId); + if (currentIndex < 0) { + setActiveLayoutId(enabledLayouts.get(0)); + return; + } + setActiveLayoutId( + enabledLayouts.get((currentIndex + 1) % enabledLayouts.size()) + ); } public void setActiveLayoutId(@NotNull KeyboardLayoutId activeLayoutId) { @@ -164,6 +175,18 @@ public void setActiveLayoutId(@NotNull KeyboardLayoutId activeLayoutId) { } } + public @NotNull List getEnabledLayoutIds() { + List enabledLayouts = VRClientSettings.getEnabledKeyboardLayouts(); + if (enabledLayouts.isEmpty()) { + return List.of(KeyboardLayoutId.EN_US); + } + return enabledLayouts; + } + + public boolean hasMultipleLayouts() { + return getEnabledLayoutIds().size() > 1; + } + private void orient(@Nullable Screen attachedTo) { if (!shown) { this.attachedTo = null; diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java index debbd5d2..4fb01b0d 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java @@ -86,15 +86,15 @@ public void init() { .build() ); //LANGUAGE - this.addRenderableWidget( - new KeyboardButton.Builder(this, - Component.literal(layout.getSwitchLabel()), - (p) -> overlayKeyboard.cycleLayout()) - .size(languageButtonWidth, keyHeight) - .pos(3 * (sideButtonWidth + keyGap) + gridStart, gridStart - (keyHeight + keyGap)) - .usePressTask(false) - .build() - ); + KeyboardButton languageButton = new KeyboardButton.Builder(this, + Component.literal(layout.getSwitchLabel()), + (p) -> overlayKeyboard.cycleLayout()) + .size(languageButtonWidth, keyHeight) + .pos(3 * (sideButtonWidth + keyGap) + gridStart, gridStart - (keyHeight + keyGap)) + .usePressTask(false) + .build(); + languageButton.active = overlayKeyboard.hasMultipleLayouts(); + this.addRenderableWidget(languageButton); //SPACE this.addRenderableWidget( new KeyboardButton.Builder(this, diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/VRSettingsControls.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/VRSettingsControls.java index 0bb9c76d..c98483dd 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/VRSettingsControls.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/VRSettingsControls.java @@ -3,6 +3,7 @@ import org.vmstudio.visor.core.client.gui.screens.settings.VROptionsSet; import org.vmstudio.visor.core.client.gui.screens.settings.VRSettingsScreen; import org.vmstudio.visor.core.client.gui.screens.settings.categories.controls.VRSettingsActionSets; +import org.vmstudio.visor.core.client.gui.screens.settings.categories.controls.VRSettingsKeyboardLayouts; import org.vmstudio.visor.core.client.settings.VROptionWidgetType; import org.vmstudio.visor.core.client.gui.screens.settings.OptionWidgetEntry; import org.vmstudio.visor.core.client.gui.screens.settings.OptionWidgetPosition; @@ -39,6 +40,13 @@ protected OptionWidgetEntry[] getOptionEntries() { 0, "visor.options.controls.action_sets.button" ), + new OptionWidgetEntry( + this, + new VRSettingsKeyboardLayouts(getScreen(), this, onWidgetsChanged), + OptionWidgetPosition.LEFT, + 1, + "visor.options.controls.keyboard_layouts.button" + ), }; diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/controls/VRSettingsKeyboardLayouts.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/controls/VRSettingsKeyboardLayouts.java new file mode 100644 index 00000000..5d907f36 --- /dev/null +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/settings/categories/controls/VRSettingsKeyboardLayouts.java @@ -0,0 +1,145 @@ +package org.vmstudio.visor.core.client.gui.screens.settings.categories.controls; + +import me.phoenixra.atumvr.api.misc.color.AtumColor; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.vmstudio.visor.api.client.gui.helpers.GuiHelper; +import org.vmstudio.visor.api.client.gui.overlays.options.OptionTextures; +import org.vmstudio.visor.api.client.gui.widgets.info.WidgetInfoCheckboxList; +import org.vmstudio.visor.api.client.gui.widgets.lists.CheckboxList; +import org.vmstudio.visor.core.client.ClientContext; +import org.vmstudio.visor.core.client.gui.overlays.builtin.settings.SettingsTextures; +import org.vmstudio.visor.core.client.gui.screens.settings.OptionWidgetEntry; +import org.vmstudio.visor.core.client.gui.screens.settings.VROptionsSet; +import org.vmstudio.visor.core.client.gui.screens.settings.VRSettingsScreen; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardLayoutId; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardLayouts; +import org.vmstudio.visor.core.client.settings.VRClientSettings; +import org.vmstudio.visor.core.client.settings.VROptionWidgetType; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.vmstudio.visor.core.client.VisorClientImpl.MC; + +public class VRSettingsKeyboardLayouts extends VROptionsSet { + + private CheckboxList listWidget; + + public VRSettingsKeyboardLayouts(@NotNull VRSettingsScreen screen, + @Nullable VROptionsSet previousOptions, + @NotNull Runnable onWidgetsChanged) { + super(screen, previousOptions, onWidgetsChanged); + } + + @Override + protected VROptionWidgetType[] getOptionTypes() { + return null; + } + + @Override + protected OptionWidgetEntry[] getOptionEntries() { + return null; + } + + @Override + public List initWidgets() { + var scaleHelper = getScreen().getScaleHelper(); + + Map rawEntries = new LinkedHashMap<>(); + for (KeyboardLayoutId layoutId : KeyboardLayouts.getSelectableLayouts()) { + rawEntries.put(layoutId.name(), layoutId.getDisplayName()); + } + + List selectedIds = VRClientSettings.getEnabledKeyboardLayouts() + .stream() + .map(Enum::name) + .toList(); + + listWidget = new CheckboxList( + new WidgetInfoCheckboxList() + .pos(scaleHelper.scaledX(57), scaleHelper.scaledY(43)) + .size(scaleHelper.scaledSize(142), scaleHelper.scaledSize(90)) + .textures( + OptionTextures.GRAY_TEXTURE, + SettingsTextures.CHECKBOX_BUTTON, + SettingsTextures.CHECKBOX_BUTTON_HOVERED, + SettingsTextures.CHECKBOX_BUTTON_SELECTED, + SettingsTextures.CHECKBOX_BUTTON_HOVERED_SELECTED + ), + rawEntries, + selectedIds, + (entry) -> saveSelectedLayouts() + ); + + return getWidgets(); + } + + @Override + public List getWidgets() { + List list = new ArrayList<>(); + list.add((T) listWidget); + return list; + } + + @Override + public void onPostRender(@NotNull GuiGraphics guiGraphics, + int mouseX, int mouseY, + float partialTicks) { + var scaleHelper = getScreen().getScaleHelper(); + GuiHelper.renderScalableText( + guiGraphics, + MC.font, + Component.translatable("visor.options.controls.keyboard_layouts.title").getString(), + AtumColor.WHITE.asInt(), + scaleHelper.scaledX(84), scaleHelper.scaledY(30), + scaleHelper.scaledSize(86), scaleHelper.scaledSize(10), + true + ); + GuiHelper.renderScalableText( + guiGraphics, + MC.font, + Component.translatable("visor.options.controls.keyboard_layouts.always_enabled").getString(), + VRSettingsScreen.INACTIVE_COLOR.asInt(), + scaleHelper.scaledX(60), scaleHelper.scaledY(136), + scaleHelper.scaledSize(126), scaleHelper.scaledSize(8), + true + ); + } + + @Override + public void loadDefaults() { + VRClientSettings.setEnabledKeyboardLayouts(List.of(KeyboardLayoutId.EN_US)); + ClientContext.settingsManager.saveOptions(); + reinit(); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double delta) { + listWidget.mouseScrolled(mouseX, mouseY, delta); + return super.mouseScrolled(mouseX, mouseY, delta); + } + + private void saveSelectedLayouts() { + List enabledLayouts = new ArrayList<>(); + for (String selectedId : listWidget.getSelectedEntriesId()) { + KeyboardLayoutId layoutId = KeyboardLayoutId.byName(selectedId); + if (layoutId != null) { + enabledLayouts.add(layoutId); + } + } + VRClientSettings.setEnabledKeyboardLayouts(enabledLayouts); + ClientContext.settingsManager.saveOptions(); + } +} \ No newline at end of file diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/settings/VRClientSettings.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/settings/VRClientSettings.java index 808ad499..b2e73774 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/settings/VRClientSettings.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/settings/VRClientSettings.java @@ -7,6 +7,8 @@ import org.vmstudio.visor.api.server.SupportedMovement; import org.vmstudio.visor.api.server.VRServerSettings; import org.vmstudio.visor.core.client.VisorClientImpl; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardLayoutId; +import org.vmstudio.visor.core.client.gui.overlays.builtin.keyboard.KeyboardLayouts; import org.vmstudio.visor.core.client.player.body.VRBodyTypeHandsOnly; import org.vmstudio.visor.core.client.settings.options.VROptionField; import org.vmstudio.visor.core.client.settings.options.enums.MirrorMode; @@ -25,6 +27,9 @@ import org.joml.Quaternionfc; import org.joml.Vector3fc; +import java.util.Collection; +import java.util.List; + import static org.vmstudio.visor.core.client.VisorClientImpl.MC; public class VRClientSettings { @@ -47,6 +52,12 @@ public class VRClientSettings { @Getter @VROptionField(key = "keyboard.keysShift") protected static String keyboardKeysShift = "~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL;':\"ZXCVBNM,./?<>"; + + @Getter + @VROptionField(key = "keyboard.layouts", category = VROptionCategory.CONTROLS) + protected static String keyboardLayouts = KeyboardLayouts.serializeEnabled( + List.of(KeyboardLayoutId.EN_US) + ); //--- @@ -366,6 +377,16 @@ public static RotationMode getRotationMode() { return rotationMode; } + public static @NotNull List getEnabledKeyboardLayouts() { + return KeyboardLayouts.getEnabled(keyboardLayouts); + } + + public static void setEnabledKeyboardLayouts( + @NotNull Collection layouts + ) { + keyboardLayouts = KeyboardLayouts.serializeEnabled(layouts); + } + public static void updateThirdPersonCamera(@NotNull Vector3fc position, @NotNull Quaternionfc rotation, boolean save){ diff --git a/visor-core/src/main/resources/assets/visor/lang/en_us.json b/visor-core/src/main/resources/assets/visor/lang/en_us.json index c8d5dc8b..e8afa5e8 100644 --- a/visor-core/src/main/resources/assets/visor/lang/en_us.json +++ b/visor-core/src/main/resources/assets/visor/lang/en_us.json @@ -104,6 +104,9 @@ "visor.options.controls.left_handed.tooltip": "If left hand should be dominant", "visor.options.controls.action_sets": "Select action set", "visor.options.controls.action_sets.button": "Action Bindings...", + "visor.options.controls.keyboard_layouts.button": "Keyboard Layouts...", + "visor.options.controls.keyboard_layouts.title": "Keyboard layouts", + "visor.options.controls.keyboard_layouts.always_enabled": "If all layouts are off, English (US) is used as fallback", "visor.options.controls.action": "Action", "_comment.immersion": "----Immersion Settings----", From 5c7b09314db3ac1be0229779318f59fb7d16b08b Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Wed, 8 Apr 2026 00:29:02 +0300 Subject: [PATCH 05/15] added vr settings to menu screen (cherry picked from commit 1c98fee2de79d661588be181226a3d4997596b3c) --- .../visor/core/client/gui/screens/GameMenuScreen.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java index f1727f7d..045028f5 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java @@ -8,6 +8,7 @@ import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.network.chat.Component; import org.vmstudio.visor.core.client.ClientContext; +import org.vmstudio.visor.core.client.gui.screens.settings.VRSettingsScreen; import org.vmstudio.visor.core.client.settings.VRClientSettings; import org.vmstudio.visor.core.client.tasks.types.TaskHotBar; @@ -85,7 +86,7 @@ protected void init() { // dont touch this please 🤞 y += BTN_H + GAP + 2; y = buildContent(cx, y); - y += 6; + y += 4; addRenderableWidget( Button.builder(Component.literal("Back to Game"), b -> this.minecraft.setScreen(null)) @@ -113,8 +114,10 @@ private int buildContent(int cx, int y) { })); y += BTN_H + GAP; - addRenderableWidget(makeFullBtn("Vanilla Pause Menu", left, y, + addRenderableWidget(makeHalfBtn("Vanilla Pause Menu", left, y, b -> this.minecraft.setScreen(new PauseScreen(true)))); + addRenderableWidget(makeHalfBtn("VR Settings", right, y, + b -> this.minecraft.setScreen(new VRSettingsScreen(this)))); y += BTN_H + GAP; } From fbafeab19060633dbc0d09395375095cd76898a9 Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Sat, 11 Apr 2026 14:21:15 +0300 Subject: [PATCH 06/15] implemented overlay drag (cherry picked from commit 3a2e7c6bafff01a86605349035101f1ae93ea7df) --- .../api/client/gui/overlays/VROverlay.java | 170 ++++++++++++++++++ .../framework/VROverlayFrameBuffer.java | 47 ++++- .../overlays/framework/VROverlayScreen.java | 65 +++++-- .../screen/VROverlayScreenInScreen.java | 6 + .../api/client/player/pose/PoseAnchor.java | 7 +- .../core/client/gui/VRCursorHandlerImpl.java | 5 +- .../core/client/gui/VROverlayManagerImpl.java | 19 ++ .../overlays/builtin/VROverlayGameScreen.java | 36 +++- .../builtin/keyboard/VROverlayKeyboard.java | 18 ++ .../render/helpers/RenderGuiHelper.java | 43 +++++ 10 files changed, 387 insertions(+), 29 deletions(-) diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java index aecc2ac8..9194a308 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java @@ -7,11 +7,19 @@ import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; import org.vmstudio.visor.api.client.gui.GuiTexture; import org.vmstudio.visor.api.client.gui.overlays.options.OverlayOptionGroup; +import org.vmstudio.visor.api.client.gui.overlays.options.types.OverlayOptionsPose; +import org.vmstudio.visor.api.client.player.pose.VRPlayerPoseClient; import org.vmstudio.visor.api.common.addon.component.PrioritySupporter; import org.vmstudio.visor.api.common.addon.component.VisorComponent; +import org.vmstudio.visor.api.common.HandType; +import org.vmstudio.visor.api.common.player.VRPose; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.joml.Vector3f; +import org.joml.Vector3fc; import java.util.Collection; @@ -79,6 +87,168 @@ default boolean isInViewDistance(){ */ boolean isVisible(); + + /** + * If overlay is draggable/movable + * + * @return true/false + */ + default boolean isDraggable() { + return false; + } + + /** + * If overlay is being dragged rightt now + * + * @return true/false + */ + default boolean isBeingDragged() { + return false; + } + + /** + * Update dragging state + * + * @param dragging true/false + */ + default void setBeingDragged(boolean dragging) { + } + + /** + * Drag position offset relative to the dragging anchor + * + * @return offset + */ + default @NotNull Vector3f getDragPositionOffset() { + return new Vector3f(0, 0, -0.3f); + } + + /** + * Update drag position offset relative to the dragging anchor + * + * @param offset new offset + */ + default void setDragPositionOffset(@NotNull Vector3fc offset) { + } + + /** + * Drag rotation matrix relative to the dragging anchor + * + * @return local rotation matrix + */ + default @NotNull Matrix4f getDragRotationMatrix() { + return new Matrix4f(); + } + + /** + * Update drag rotation matrix relative to the dragging anchor + * + * @param rotationMatrix new local rotation matrix + */ + default void setDragRotationMatrix(@NotNull Matrix4fc rotationMatrix) { + } + + /** + * If specified raw cursor position is over the drag handle + * + * @param rawX raw cursor x + * @param rawY raw cursor y + * @return true/false + */ + default boolean isCursorOnDragHandle(float rawX, float rawY) { + return isDraggable() + && rawX >= 0f && rawX <= 1f + && rawY > 1.0f && rawY <= 1.15f; + } + + /** + * Start dragging this overlay with the currently active cursor hand + */ + default void startDragging() { + HandType cursorHand = VisorAPI.client().getGuiManager().getCursorHandler().getCursorHand(); + PoseAnchor dragAnchor = cursorHand == HandType.MAIN ? PoseAnchor.MAIN_HAND : PoseAnchor.OFFHAND; + VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + VRPose anchorPose = dragAnchor.getSupplier().apply(renderPose); + + Vector3f dragPositionOffset = anchorPose.reverseCustomVector( + getPose().getPosition().sub(anchorPose.getPosition(), new Vector3f()) + ).div(renderPose.getWorldScale()); + Matrix4f dragRotationMatrix = anchorPose.getRotation() + .invert(new Matrix4f()) + .mul(getPose().getRotation(), new Matrix4f()); + + setDragPositionOffset(dragPositionOffset); + setDragRotationMatrix(dragRotationMatrix); + setForcedAnchor(dragAnchor); + setBeingDragged(true); + } + + /** + * Stop dragging and persist the pose back into pose options when available + */ + default void stopDragging() { + setForcedAnchor(null); + setBeingDragged(false); + onDragStopped(); + + OverlayOptionsPose poseOptions = getOption(OverlayOptionsPose.ID, OverlayOptionsPose.class); + if (poseOptions == null) { + return; + } + + VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + + PoseAnchor posAnchor = poseOptions.getPositionAnchor(); + VRPose posAnchorPose = posAnchor.getSupplier().apply(renderPose); + Vector3f offsetPos = posAnchorPose.reverseCustomVector( + getPose().getPosition().sub(posAnchorPose.getPosition(), new Vector3f()) + ).div(renderPose.getWorldScale()); + + poseOptions.setPositionOffset(offsetPos); + + if (!poseOptions.isAimedRotation()) { + PoseAnchor rotAnchor = poseOptions.getRotationAnchor(); + VRPose rotAnchorPose = rotAnchor.getSupplier().apply(renderPose); + Vector3f rotOffset = rotAnchor.reverseAnchoredRotation( + rotAnchorPose.getRotation(), getPose().getRotation() + ); + poseOptions.setRotationOffset(rotOffset); + } + + poseOptions.save(); + } + + /** + * Hook called when dragging stops, before pose options are persisted + */ + default void onDragStopped() { + } + + /** + * Apply current drag transform from forced anchor + */ + default void applyDraggedPose() { + PoseAnchor dragAnchor = getForcedAnchor(); + if (dragAnchor == null) { + return; + } + + VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + VRPose anchorPose = dragAnchor.getSupplier().apply(renderPose); + + Vector3f positionOffset = new Vector3f(getDragPositionOffset()).mul(renderPose.getWorldScale()); + Vector3f newPosition = anchorPose.getCustomVector(positionOffset) + .add(anchorPose.getPosition()); + Matrix4f newRotation = new Matrix4f(anchorPose.getRotation()) + .mul(getDragRotationMatrix(), new Matrix4f()); + + getPose().update( + newPosition, + newRotation, + getPose().getScale() + ); + } + /** * If this overlay is custom, * i.e. created from template diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java index 6aaba7d8..a832aa72 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java @@ -13,7 +13,10 @@ import org.vmstudio.visor.api.common.addon.VisorAddon; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; import org.joml.Vector3f; +import org.joml.Vector3fc; import java.io.IOException; import java.util.*; @@ -66,6 +69,10 @@ public abstract class VROverlayFrameBuffer implements VROverlay { @Getter private boolean visible = false; + private boolean beingDragged = false; + private Vector3f dragPositionOffset = new Vector3f(0, 0, -0.3f); + private Matrix4f dragRotationMatrix = new Matrix4f(); + public VROverlayFrameBuffer(@NotNull VisorAddon owner, @NotNull String id){ @@ -174,15 +181,7 @@ public void render(float partialTick){ @Override public final void updatePose(float partialTicks) { if(forcedAnchor != null) { - VROverlayHelper.applyPose( - this, - forcedAnchor, - forcedAnchor, - getPose().getScale(), - false, - new Vector3f(0,0,-0.3f), - new Vector3f(0,0,0) - ); + applyDraggedPose(); return; } onUpdatePose(partialTicks); @@ -208,6 +207,36 @@ public void setEnabled(boolean flag) { return optionsMap.get(id); } + @Override + public boolean isBeingDragged() { + return beingDragged; + } + + @Override + public void setBeingDragged(boolean dragging) { + this.beingDragged = dragging; + } + + @Override + public @NotNull Vector3f getDragPositionOffset() { + return new Vector3f(dragPositionOffset); + } + + @Override + public void setDragPositionOffset(@NotNull Vector3fc offset) { + this.dragPositionOffset.set(offset); + } + + @Override + public @NotNull Matrix4f getDragRotationMatrix() { + return new Matrix4f(dragRotationMatrix); + } + + @Override + public void setDragRotationMatrix(@NotNull Matrix4fc rotationMatrix) { + this.dragRotationMatrix.set(rotationMatrix); + } + @Override public boolean supportsCursor() { return false; diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java index d2e22dd9..1ede6d96 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java @@ -18,7 +18,10 @@ import net.minecraft.util.Mth; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; import org.joml.Vector3f; +import org.joml.Vector3fc; import java.io.IOException; import java.util.*; @@ -89,6 +92,10 @@ public abstract class VROverlayScreen extends Screen implements VROverlay { private static long mouseDragDelay; + private boolean beingDragged = false; + private Vector3f dragPositionOffset = new Vector3f(0, 0, -0.3f); + private Matrix4f dragRotationMatrix = new Matrix4f(); + public VROverlayScreen(@NotNull VisorAddon owner, @@ -249,15 +256,7 @@ public final void render(@NotNull GuiGraphics guiGraphics, @Override public final void updatePose(float partialTicks) { if(forcedAnchor != null) { - VROverlayHelper.applyPose( - this, - forcedAnchor, - forcedAnchor, - getPose().getScale(), - false, - new Vector3f(0,0,-0.3f), - new Vector3f(0,0,0) - ); + applyDraggedPose(); return; } onUpdatePose(partialTicks); @@ -314,9 +313,11 @@ public void updateSize(){ @Override public void updateCursorData(boolean activeCursor, float rawX, float rawY) { if (!enabled) return; - if (rawX < 0f || rawX > 1f - || rawY < 0f || rawY > 1f) { - return; + boolean withinGui = rawX >= 0f && rawX <= 1f + && rawY >= 0f && rawY <= 1f; + boolean onDragHandle = isCursorOnDragHandle(rawX, rawY); + if (!withinGui && !onDragHandle) { + return; } // ---- Preparing @@ -342,6 +343,10 @@ public void updateCursorData(boolean activeCursor, float rawX, float rawY) { return; } + if (!withinGui) { + return; + } + // ---- Move and Drag events mouseMoved(cursorData.getCursorX(), cursorData.getCursorY()); @@ -357,7 +362,35 @@ public void updateCursorData(boolean activeCursor, float rawX, float rawY) { } + @Override + public boolean isBeingDragged() { + return beingDragged; + } + + @Override + public void setBeingDragged(boolean dragging) { + this.beingDragged = dragging; + } + + @Override + public @NotNull Vector3f getDragPositionOffset() { + return new Vector3f(dragPositionOffset); + } + + @Override + public void setDragPositionOffset(@NotNull Vector3fc offset) { + this.dragPositionOffset.set(offset); + } + + @Override + public @NotNull Matrix4f getDragRotationMatrix() { + return new Matrix4f(dragRotationMatrix); + } + @Override + public void setDragRotationMatrix(@NotNull Matrix4fc rotationMatrix) { + this.dragRotationMatrix.set(rotationMatrix); + } public boolean isVisible() { @@ -372,10 +405,18 @@ public boolean isVisible() { @Override public boolean mouseClicked(double mouseX, double mouseY, int buttonType) { + if (buttonType == 0 && isCursorOnDragHandle(getRawMouseX(), getRawMouseY())) { + startDragging(); + return true; + } return super.mouseClicked(mouseX, mouseY, buttonType); } @Override public boolean mouseReleased(double mouseX, double mouseY, int buttonType) { + if (isBeingDragged() && buttonType == 0) { + stopDragging(); + return true; + } return super.mouseReleased(mouseX, mouseY, buttonType); } @Override diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java index 8c89fdc5..f880617e 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java @@ -61,12 +61,18 @@ protected void onRender(GuiGraphics guiGraphics, @Override public boolean mouseClicked(double mouseX, double mouseY, int buttonType) { + if (super.mouseClicked(mouseX, mouseY, buttonType)) { + return true; + } if(screen==null) return true; return screen.mouseClicked(mouseX, mouseY, buttonType); } @Override public boolean mouseReleased(double mouseX, double mouseY, int buttonType) { + if (super.mouseReleased(mouseX, mouseY, buttonType)) { + return true; + } if(screen==null) return true; return screen.mouseReleased(mouseX, mouseY, buttonType); } diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/player/pose/PoseAnchor.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/player/pose/PoseAnchor.java index c2fa6621..dfa5a825 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/player/pose/PoseAnchor.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/player/pose/PoseAnchor.java @@ -125,11 +125,8 @@ public enum PoseAnchor { public @NotNull Vector3f reverseAnchoredRotation(@NotNull Matrix4fc anchorRotation, @NotNull Matrix4fc objRotation) { - - Matrix4f invRotation = objRotation.invert(new Matrix4f()); - - - Matrix4f matrix4f = invRotation.mul(anchorRotation); + Matrix4f matrix4f = anchorRotation.invert(new Matrix4f()) + .mul(objRotation, new Matrix4f()); float offsetY = (float) Math.asin(-matrix4f.m20()); float offsetZ = (float) Mth.atan2(matrix4f.m10(), matrix4f.m00()); diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VRCursorHandlerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VRCursorHandlerImpl.java index 65037f73..b4625e9b 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VRCursorHandlerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VRCursorHandlerImpl.java @@ -180,7 +180,10 @@ private void updateOverlays() { cursorPos.x(), cursorPos.y() ); - if (withinBounds) { + + boolean onDragHandle = overlay.isCursorOnDragHandle(cursorPos.x(), cursorPos.y()); + + if (withinBounds || onDragHandle) { finalCursorPos = cursorPos; collidingOverlay = overlay; closestDistance = cursorPos.z(); diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java index 086c0d9c..f3fc5975 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java @@ -216,6 +216,16 @@ public void renderDepthOverlays(float partialTicks, overlay.supportsLight(), overlay.getPose().getScale() ); + + if(overlay.isDraggable()) { + RenderGuiHelper.renderDragHandle( + overlay, + poseStack, + overlay.getPose().getPosition(), + overlay.getPose().getRotation(), + overlay.getPose().getScale() + ); + } GLUtils.checkGLError("post depth VROverlay quad: " + overlay.getId()); } @@ -260,6 +270,15 @@ public void renderHudOverlays(float partialTicks, overlay.supportsLight(), overlay.getPose().getScale() ); + if(overlay.isDraggable()) { + RenderGuiHelper.renderDragHandle( + overlay, + poseStack, + overlay.getPose().getPosition(), + overlay.getPose().getRotation(), + overlay.getPose().getScale() + ); + } GLUtils.checkGLError("post hud VROverlay quad: " + overlay.getId()); } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java index 59857a71..92062ce3 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java @@ -204,8 +204,10 @@ public void updateCursorData(boolean activeCursor, if (!isEnabled()) return; if(!activeCursor) return; - if (rawX < 0f || rawX > 1f - || rawY < 0f || rawY > 1f) { + boolean withinGui = rawX >= 0f && rawX <= 1f + && rawY >= 0f && rawY <= 1f; + boolean onDragHandle = isCursorOnDragHandle(rawX, rawY); + if (!withinGui && !onDragHandle) { //do nothing. If we change mouse position here // to emulate mouse exiting the screen, bugs appear // (todo find a way to emulate without bugs) @@ -235,6 +237,10 @@ public void updateCursorData(boolean activeCursor, cursorData.setCursorX((int)(rawX * (double) guiScaledWidth)); cursorData.setCursorY((int)(rawY * (double) guiScaledHeight)); + if (!withinGui) { + return; + } + //here as an input it requires NOT SCALED position InputHelper.setMousePos( (int)(rawX * (double) screenWidth), @@ -258,8 +264,26 @@ public boolean willBeInMenuRoom(Screen newScreen) { MC.getOverlay() != null; } + @Override + public void onDragStopped() { + var relativePose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE); + relativePosition = relativePose.convertPositionFrom( + PlayerPoseType.RENDER, + getPose().getPosition() + ); + relativeRotation = relativePose.convertRotationFrom( + PlayerPoseType.RENDER, + getPose().getRotation() + ); + overlayScale = getPose().getScale(); + } + @Override public boolean mouseClicked(double x, double y, int buttonType) { + if (buttonType == 0 && isCursorOnDragHandle(getRawMouseX(), getRawMouseY())) { + startDragging(); + return true; + } //we need it to go through minecraft InputHelper.pressMouse(MouseButtonType.fromId(buttonType)); return true; @@ -267,6 +291,10 @@ public boolean mouseClicked(double x, double y, int buttonType) { @Override public boolean mouseReleased(double mouseX, double mouseY, int buttonType) { + if (buttonType == 0 && isBeingDragged()) { + stopDragging(); + return true; + } //we need it to go through minecraft InputHelper.releaseMouse(MouseButtonType.fromId(buttonType)); return true; @@ -299,4 +327,8 @@ public boolean supportsCursor() { return Component.translatable("visor.overlay.%s.description".formatted(getId())); } + @Override + public boolean isDraggable() { + return true; + } } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java index f095ea90..60d39159 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java @@ -127,11 +127,29 @@ public void onUpdatePose(float partialTicks) { ); } + @Override + public void onDragStopped() { + var relativePose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE); + relativePosition = relativePose.convertPositionFrom( + PlayerPoseType.RENDER, + getPose().getPosition() + ); + relativeRotation = relativePose.convertRotationFrom( + PlayerPoseType.RENDER, + getPose().getRotation() + ); + } + @Override public boolean supportsTwoCursors() { return true; } + @Override + public boolean isDraggable() { + return true; + } + public void setVisible(boolean flag, @Nullable Screen attachedTo) { diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java index d606c8ec..8836cc5a 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import me.phoenixra.atumvr.api.misc.color.AtumColor; +import net.minecraft.client.renderer.GameRenderer; import org.vmstudio.visor.api.client.player.pose.VRPlayerPoseClient; import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; import org.vmstudio.visor.api.client.gui.overlays.VROverlay; @@ -157,6 +158,48 @@ public static void renderOverlayQuad(VROverlay overlay, } + // todo: redesign + public static void renderDragHandle(VROverlay overlay, PoseStack poseStack, Vector3fc position, Matrix4fc orientation, float scale) { + VRPlayerPoseClient renderPose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RENDER); + var eye = RenderPoseHelper.getCameraPosition(VRRenderState.getRenderPass(), renderPose); + float finalScale = scale * renderPose.getWorldScale(); + AtumColor color = overlay.isBeingDragged() + ? AtumColor.immutable(150, 150, 150, 255) + : AtumColor.immutable(100, 100, 100, 255); + + RenderSystem.disableCull(); + RenderSystem.setShader(GameRenderer::getPositionColorShader); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.depthMask(true); + + poseStack.pushPose(); + poseStack.translate(position.x() - eye.x(), position.y() - eye.y(), position.z() - eye.z()); + poseStack.mulPoseMatrix((Matrix4f) orientation); + poseStack.scale(finalScale, finalScale, finalScale); + + float aspect = overlay.getAspectRatio(); + float halfSize = VROverlayPose.QUAD_SCALE * 0.5f; + float halfHeight = halfSize * aspect; + + float handleTop = -halfHeight; + float handleBottom = -halfHeight - (halfHeight * 0.3f); + + float r = color.getRed(), g = color.getGreen(), b = color.getBlue(), a = color.getAlpha(); + + BufferBuilder buf = Tesselator.getInstance().getBuilder(); + buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + + buf.vertex(poseStack.last().pose(), -halfSize, handleBottom, 0f).color(r, g, b, a).endVertex(); + buf.vertex(poseStack.last().pose(), halfSize, handleBottom, 0f).color(r, g, b, a).endVertex(); + buf.vertex(poseStack.last().pose(), halfSize, handleTop, 0f).color(r, g, b, a).endVertex(); + buf.vertex(poseStack.last().pose(), -halfSize, handleTop, 0f).color(r, g, b, a).endVertex(); + + BufferUploader.drawWithShader(buf.end()); + + RenderSystem.enableCull(); + poseStack.popPose(); + } } From 02e3c14ef694f70c333e3d55ed7917f2cc8fb334 Mon Sep 17 00:00:00 2001 From: Weever1337 Date: Sat, 11 Apr 2026 14:25:15 +0300 Subject: [PATCH 07/15] replaced "Back to game" to "Save and Quit to Title" due to design issue (cherry picked from commit 60007f3b25be219de6bd3ae00beab65a7abaa900) --- .../client/gui/screens/GameMenuScreen.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java index 045028f5..0884a162 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java @@ -1,11 +1,11 @@ package org.vmstudio.visor.core.client.gui.screens; +import com.mojang.realmsclient.RealmsMainScreen; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.screens.ChatScreen; -import net.minecraft.client.gui.screens.PauseScreen; -import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.network.chat.Component; import org.vmstudio.visor.core.client.ClientContext; import org.vmstudio.visor.core.client.gui.screens.settings.VRSettingsScreen; @@ -89,7 +89,7 @@ protected void init() { y += 4; addRenderableWidget( - Button.builder(Component.literal("Back to Game"), b -> this.minecraft.setScreen(null)) + Button.builder(Component.literal("Save and Quit to Title"), b -> this.minecraft.getReportingContext().draftReportHandled(this.minecraft, this, this::onDisconnect, true)) .pos(cx - COLUMN_W / 2, y).width(COLUMN_W).build() ); } @@ -253,6 +253,28 @@ private void sendCommand(String command) { } } + // took from PauseScreen + private void onDisconnect() { + boolean bl = this.minecraft.isLocalServer(); + boolean bl2 = this.minecraft.isConnectedToRealms(); + this.minecraft.level.disconnect(); + if (bl) { + this.minecraft.clearLevel(new GenericDirtMessageScreen(Component.literal("Saving world"))); + } else { + this.minecraft.clearLevel(); + } + + TitleScreen titleScreen = new TitleScreen(); + if (bl) { + this.minecraft.setScreen(titleScreen); + } else if (bl2) { + this.minecraft.setScreen(new RealmsMainScreen(titleScreen)); + } else { + this.minecraft.setScreen(new JoinMultiplayerScreen(titleScreen)); + } + + } + @Override public boolean isPauseScreen() { return false; From 369ae9aa4ecff2346684effd1760bbf28bca95b8 Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:06:24 +0400 Subject: [PATCH 08/15] improved api: cleaner, no dragging for game screen in main-menu, no super call on mouseClicked, mouseReleased in VROverlayScreenInScreen (that call may cause issues potentially, since the idea of this overlay is to work through the other screen, not its own) --- .../api/client/gui/overlays/VROverlay.java | 246 +++++------------- .../framework/VROverlayFrameBuffer.java | 103 ++++++-- .../overlays/framework/VROverlayScreen.java | 135 +++++++--- .../screen/VROverlayScreenInScreen.java | 6 +- .../core/client/gui/VROverlayManagerImpl.java | 4 +- .../overlays/builtin/VROverlayGameScreen.java | 12 +- .../builtin/keyboard/VROverlayKeyboard.java | 4 +- 7 files changed, 264 insertions(+), 246 deletions(-) diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java index 9194a308..59e97e94 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java @@ -7,19 +7,13 @@ import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; import org.vmstudio.visor.api.client.gui.GuiTexture; import org.vmstudio.visor.api.client.gui.overlays.options.OverlayOptionGroup; -import org.vmstudio.visor.api.client.gui.overlays.options.types.OverlayOptionsPose; -import org.vmstudio.visor.api.client.player.pose.VRPlayerPoseClient; import org.vmstudio.visor.api.common.addon.component.PrioritySupporter; import org.vmstudio.visor.api.common.addon.component.VisorComponent; -import org.vmstudio.visor.api.common.HandType; -import org.vmstudio.visor.api.common.player.VRPose; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import org.joml.Matrix4fc; import org.joml.Vector3f; -import org.joml.Vector3fc; import java.util.Collection; @@ -88,167 +82,6 @@ default boolean isInViewDistance(){ boolean isVisible(); - /** - * If overlay is draggable/movable - * - * @return true/false - */ - default boolean isDraggable() { - return false; - } - - /** - * If overlay is being dragged rightt now - * - * @return true/false - */ - default boolean isBeingDragged() { - return false; - } - - /** - * Update dragging state - * - * @param dragging true/false - */ - default void setBeingDragged(boolean dragging) { - } - - /** - * Drag position offset relative to the dragging anchor - * - * @return offset - */ - default @NotNull Vector3f getDragPositionOffset() { - return new Vector3f(0, 0, -0.3f); - } - - /** - * Update drag position offset relative to the dragging anchor - * - * @param offset new offset - */ - default void setDragPositionOffset(@NotNull Vector3fc offset) { - } - - /** - * Drag rotation matrix relative to the dragging anchor - * - * @return local rotation matrix - */ - default @NotNull Matrix4f getDragRotationMatrix() { - return new Matrix4f(); - } - - /** - * Update drag rotation matrix relative to the dragging anchor - * - * @param rotationMatrix new local rotation matrix - */ - default void setDragRotationMatrix(@NotNull Matrix4fc rotationMatrix) { - } - - /** - * If specified raw cursor position is over the drag handle - * - * @param rawX raw cursor x - * @param rawY raw cursor y - * @return true/false - */ - default boolean isCursorOnDragHandle(float rawX, float rawY) { - return isDraggable() - && rawX >= 0f && rawX <= 1f - && rawY > 1.0f && rawY <= 1.15f; - } - - /** - * Start dragging this overlay with the currently active cursor hand - */ - default void startDragging() { - HandType cursorHand = VisorAPI.client().getGuiManager().getCursorHandler().getCursorHand(); - PoseAnchor dragAnchor = cursorHand == HandType.MAIN ? PoseAnchor.MAIN_HAND : PoseAnchor.OFFHAND; - VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); - VRPose anchorPose = dragAnchor.getSupplier().apply(renderPose); - - Vector3f dragPositionOffset = anchorPose.reverseCustomVector( - getPose().getPosition().sub(anchorPose.getPosition(), new Vector3f()) - ).div(renderPose.getWorldScale()); - Matrix4f dragRotationMatrix = anchorPose.getRotation() - .invert(new Matrix4f()) - .mul(getPose().getRotation(), new Matrix4f()); - - setDragPositionOffset(dragPositionOffset); - setDragRotationMatrix(dragRotationMatrix); - setForcedAnchor(dragAnchor); - setBeingDragged(true); - } - - /** - * Stop dragging and persist the pose back into pose options when available - */ - default void stopDragging() { - setForcedAnchor(null); - setBeingDragged(false); - onDragStopped(); - - OverlayOptionsPose poseOptions = getOption(OverlayOptionsPose.ID, OverlayOptionsPose.class); - if (poseOptions == null) { - return; - } - - VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); - - PoseAnchor posAnchor = poseOptions.getPositionAnchor(); - VRPose posAnchorPose = posAnchor.getSupplier().apply(renderPose); - Vector3f offsetPos = posAnchorPose.reverseCustomVector( - getPose().getPosition().sub(posAnchorPose.getPosition(), new Vector3f()) - ).div(renderPose.getWorldScale()); - - poseOptions.setPositionOffset(offsetPos); - - if (!poseOptions.isAimedRotation()) { - PoseAnchor rotAnchor = poseOptions.getRotationAnchor(); - VRPose rotAnchorPose = rotAnchor.getSupplier().apply(renderPose); - Vector3f rotOffset = rotAnchor.reverseAnchoredRotation( - rotAnchorPose.getRotation(), getPose().getRotation() - ); - poseOptions.setRotationOffset(rotOffset); - } - - poseOptions.save(); - } - - /** - * Hook called when dragging stops, before pose options are persisted - */ - default void onDragStopped() { - } - - /** - * Apply current drag transform from forced anchor - */ - default void applyDraggedPose() { - PoseAnchor dragAnchor = getForcedAnchor(); - if (dragAnchor == null) { - return; - } - - VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); - VRPose anchorPose = dragAnchor.getSupplier().apply(renderPose); - - Vector3f positionOffset = new Vector3f(getDragPositionOffset()).mul(renderPose.getWorldScale()); - Vector3f newPosition = anchorPose.getCustomVector(positionOffset) - .add(anchorPose.getPosition()); - Matrix4f newRotation = new Matrix4f(anchorPose.getRotation()) - .mul(getDragRotationMatrix(), new Matrix4f()); - - getPose().update( - newPosition, - newRotation, - getPose().getScale() - ); - } - /** * If this overlay is custom, * i.e. created from template @@ -269,6 +102,18 @@ default boolean isBuiltIn(){ return asTemplate() == null; } + /** + * Get this overlay as template + * + * @return overlay template or null if not an instance of {@link VROverlayTemplate} + */ + default @Nullable VROverlayTemplate asTemplate(){ + if(this instanceof VROverlayTemplate overlayTemplate){ + return overlayTemplate; + }else{ + return null; + } + } /** * Get overlay name @@ -299,6 +144,11 @@ default Component getDescription(){ } + + //--------------------------- + //--------- OPTIONS --------- + //--------------------------- + /** * Get collection of overlay options * @@ -387,6 +237,9 @@ default void reloadOption(@NotNull String id){ + //--------------------------------------------- + //--------- FORCED ANCHOR && DRAGGING --------- + //--------------------------------------------- /** * Get the forced anchor @@ -412,19 +265,42 @@ default void reloadOption(@NotNull String id){ /** - * Get this overlay as template + * Start dragging this overlay with the currently active cursor hand + */ + void startDragging(); + + /** + * Stop dragging and persist the pose back into pose options when available + */ + void stopDragging(); + + /** + * If overlay is being dragged rightt now * - * @return overlay template or null if not an instance of {@link VROverlayTemplate} + * @return true/false */ - default @Nullable VROverlayTemplate asTemplate(){ - if(this instanceof VROverlayTemplate overlayTemplate){ - return overlayTemplate; - }else{ - return null; - } + default boolean isBeingDragged() { + return false; + } + + /** + * If specified raw cursor position is over the drag handle + * + * @param rawX raw cursor x + * @param rawY raw cursor y + * @return true/false + */ + default boolean isCursorOnDragHandle(float rawX, float rawY) { + return supportsDragging() + && rawX >= 0f && rawX <= 1f + && rawY > 1.0f && rawY <= 1.15f; } + //-------------------------------------- + //--------- SUPPORTED FEATURES --------- + //-------------------------------------- + /** * If supports update of visibility each render call, instead of tick() * @return true/false @@ -486,7 +362,19 @@ default boolean supportsTwoCursors(){ return false; } + /** + * If overlay can be dragged and repositioned by the player + * + * @return true/false + */ + default boolean supportsDragging() { + return false; + } + + //---------------------------------------- + //--------- CURSOR && RESOLUTION --------- + //---------------------------------------- /** * Get Data for active cursor @@ -642,6 +530,10 @@ default boolean isWithinCursorBounds(float rawX, float rawY) { } + //---------------------------- + //--------- MC STUFF --------- + //---------------------------- + /** * Get active cursor position X * @@ -805,6 +697,10 @@ boolean mouseDragged(double mouseX, double mouseY, boolean charTyped(char chr, int modifiers); + //------------------------- + //--------- EXTRA --------- + //------------------------- + /** * Override of {@link PrioritySupporter#compareTo(PrioritySupporter)} * to sort components in reverse priority order, diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java index a832aa72..f4978caa 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayFrameBuffer.java @@ -6,8 +6,12 @@ import me.phoenixra.atumconfig.api.config.ConfigFile; import org.vmstudio.visor.api.VisorAPI; import org.vmstudio.visor.api.client.gui.overlays.*; +import org.vmstudio.visor.api.client.gui.overlays.options.types.OverlayOptionsPose; +import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; import org.vmstudio.visor.api.client.player.pose.PoseAnchor; import org.vmstudio.visor.api.client.gui.overlays.options.OverlayOptionGroup; +import org.vmstudio.visor.api.client.player.pose.VRPlayerPoseClient; +import org.vmstudio.visor.api.common.HandType; import org.vmstudio.visor.api.common.VRException; import org.vmstudio.visor.api.common.addon.component.ComponentPriority; import org.vmstudio.visor.api.common.addon.VisorAddon; @@ -17,6 +21,7 @@ import org.joml.Matrix4fc; import org.joml.Vector3f; import org.joml.Vector3fc; +import org.vmstudio.visor.api.common.player.VRPose; import java.io.IOException; import java.util.*; @@ -134,9 +139,10 @@ protected void onRender(float partialTicks) {} protected abstract void onUpdatePose(float partialTicks); - protected abstract boolean updateVisibility(); + protected void onStoppedDragging() {}; + protected void onEnable() {} protected void onDisable() {} @@ -181,7 +187,7 @@ public void render(float partialTick){ @Override public final void updatePose(float partialTicks) { if(forcedAnchor != null) { - applyDraggedPose(); + applyForcedPose(); return; } onUpdatePose(partialTicks); @@ -202,41 +208,96 @@ public void setEnabled(boolean flag) { } } - @Override - public @Nullable OverlayOptionGroup getOption(@NotNull String id) { - return optionsMap.get(id); - } @Override - public boolean isBeingDragged() { - return beingDragged; + public void startDragging() { + var vrClient = VisorAPI.client(); + + HandType cursorHand = vrClient.getGuiManager().getCursorHandler().getCursorHand(); + PoseAnchor dragAnchor = cursorHand == HandType.MAIN + ? PoseAnchor.MAIN_HAND + : PoseAnchor.OFFHAND; + VRPlayerPoseClient renderPose = vrClient.getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + VRPose anchorPose = dragAnchor.getSupplier().apply(renderPose); + + Vector3f dragPositionOffset = anchorPose.reverseCustomVector( + getPose().getPosition().sub(anchorPose.getPosition(), new Vector3f()) + ).div(renderPose.getWorldScale()); + Matrix4f dragRotation = anchorPose.getRotation() + .invert(new Matrix4f()) + .mul(getPose().getRotation(), new Matrix4f()); + + this.dragPositionOffset.set(dragPositionOffset); + this.dragRotationMatrix.set(dragRotation); + this.beingDragged = true; + setForcedAnchor(dragAnchor); } @Override - public void setBeingDragged(boolean dragging) { - this.beingDragged = dragging; - } + public void stopDragging() { + setForcedAnchor(null); + this.beingDragged = false; + + OverlayOptionsPose poseOptions = getOption(OverlayOptionsPose.ID, OverlayOptionsPose.class); + if (poseOptions != null) { + VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + + PoseAnchor posAnchor = poseOptions.getPositionAnchor(); + VRPose posAnchorPose = posAnchor.getSupplier().apply(renderPose); + Vector3f offsetPos = posAnchorPose.reverseCustomVector( + getPose().getPosition().sub(posAnchorPose.getPosition(), new Vector3f()) + ).div(renderPose.getWorldScale()); + + poseOptions.setPositionOffset(offsetPos); + + if (!poseOptions.isAimedRotation()) { + PoseAnchor rotAnchor = poseOptions.getRotationAnchor(); + VRPose rotAnchorPose = rotAnchor.getSupplier().apply(renderPose); + Vector3f rotOffset = rotAnchor.reverseAnchoredRotation( + rotAnchorPose.getRotation(), getPose().getRotation() + ); + poseOptions.setRotationOffset(rotOffset); + } - @Override - public @NotNull Vector3f getDragPositionOffset() { - return new Vector3f(dragPositionOffset); + poseOptions.save(); + } + + onStoppedDragging(); } - @Override - public void setDragPositionOffset(@NotNull Vector3fc offset) { - this.dragPositionOffset.set(offset); + protected void applyForcedPose() { + if (forcedAnchor == null) { + return; + } + + VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + VRPose anchorPose = forcedAnchor.getSupplier().apply(renderPose); + + Vector3f positionOffset = new Vector3f(dragPositionOffset) + .mul(renderPose.getWorldScale()); + Vector3f newPosition = anchorPose.getCustomVector(positionOffset) + .add(anchorPose.getPosition()); + Matrix4f newRotation = new Matrix4f(anchorPose.getRotation()) + .mul(dragRotationMatrix, new Matrix4f()); + + getPose().update( + newPosition, + newRotation, + getPose().getScale() + ); } @Override - public @NotNull Matrix4f getDragRotationMatrix() { - return new Matrix4f(dragRotationMatrix); + public @Nullable OverlayOptionGroup getOption(@NotNull String id) { + return optionsMap.get(id); } @Override - public void setDragRotationMatrix(@NotNull Matrix4fc rotationMatrix) { - this.dragRotationMatrix.set(rotationMatrix); + public boolean isBeingDragged() { + return beingDragged; } + @Override public boolean supportsCursor() { return false; diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java index 1ede6d96..978c8dfc 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java @@ -6,8 +6,12 @@ import me.phoenixra.atumconfig.api.config.ConfigFile; import org.vmstudio.visor.api.VisorAPI; import org.vmstudio.visor.api.client.gui.overlays.*; +import org.vmstudio.visor.api.client.gui.overlays.options.types.OverlayOptionsPose; +import org.vmstudio.visor.api.client.player.pose.PlayerPoseType; import org.vmstudio.visor.api.client.player.pose.PoseAnchor; import org.vmstudio.visor.api.client.gui.overlays.options.OverlayOptionGroup; +import org.vmstudio.visor.api.client.player.pose.VRPlayerPoseClient; +import org.vmstudio.visor.api.common.HandType; import org.vmstudio.visor.api.common.VRException; import org.vmstudio.visor.api.common.addon.component.ComponentPriority; import org.vmstudio.visor.api.common.addon.VisorAddon; @@ -19,9 +23,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import org.joml.Matrix4fc; import org.joml.Vector3f; -import org.joml.Vector3fc; +import org.vmstudio.visor.api.common.player.VRPose; import java.io.IOException; import java.util.*; @@ -162,9 +165,10 @@ protected void onRender(GuiGraphics guiGraphics, protected abstract void onUpdatePose(float partialTicks); - protected abstract boolean updateVisibility(); + protected void onStoppedDragging() {}; + protected void onEnable() {}; protected void onDisable() {}; @@ -256,7 +260,7 @@ public final void render(@NotNull GuiGraphics guiGraphics, @Override public final void updatePose(float partialTicks) { if(forcedAnchor != null) { - applyDraggedPose(); + applyForcedPose(); return; } onUpdatePose(partialTicks); @@ -288,15 +292,6 @@ public void setEnabled(boolean flag) { } } - public boolean canDragMouse(){ - return mouseDragDelay < System.currentTimeMillis(); - } - public void startDragMouse(){ - mouseDragDelay = System.currentTimeMillis() + 100L; - } - public void finishDragMouse(){ - mouseDragDelay = Long.MAX_VALUE; - } public void updateSize(){ guiScaleFactor = VisorAPI.client().getGuiManager().calculateScale( 0, @@ -310,6 +305,93 @@ public void updateSize(){ ); } + @Override + public void startDragging() { + var vrClient = VisorAPI.client(); + + HandType cursorHand = vrClient.getGuiManager().getCursorHandler().getCursorHand(); + PoseAnchor dragAnchor = cursorHand == HandType.MAIN + ? PoseAnchor.MAIN_HAND + : PoseAnchor.OFFHAND; + VRPlayerPoseClient renderPose = vrClient.getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + VRPose anchorPose = dragAnchor.getSupplier().apply(renderPose); + + Vector3f dragPositionOffset = anchorPose.reverseCustomVector( + getPose().getPosition().sub(anchorPose.getPosition(), new Vector3f()) + ).div(renderPose.getWorldScale()); + Matrix4f dragRotation = anchorPose.getRotation() + .invert(new Matrix4f()) + .mul(getPose().getRotation(), new Matrix4f()); + + this.dragPositionOffset.set(dragPositionOffset); + this.dragRotationMatrix.set(dragRotation); + this.beingDragged = true; + setForcedAnchor(dragAnchor); + } + + public void stopDragging() { + setForcedAnchor(null); + this.beingDragged = false; + + OverlayOptionsPose poseOptions = getOption(OverlayOptionsPose.ID, OverlayOptionsPose.class); + if (poseOptions != null) { + VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + + PoseAnchor posAnchor = poseOptions.getPositionAnchor(); + VRPose posAnchorPose = posAnchor.getSupplier().apply(renderPose); + Vector3f offsetPos = posAnchorPose.reverseCustomVector( + getPose().getPosition().sub(posAnchorPose.getPosition(), new Vector3f()) + ).div(renderPose.getWorldScale()); + + poseOptions.setPositionOffset(offsetPos); + + if (!poseOptions.isAimedRotation()) { + PoseAnchor rotAnchor = poseOptions.getRotationAnchor(); + VRPose rotAnchorPose = rotAnchor.getSupplier().apply(renderPose); + Vector3f rotOffset = rotAnchor.reverseAnchoredRotation( + rotAnchorPose.getRotation(), getPose().getRotation() + ); + poseOptions.setRotationOffset(rotOffset); + } + + poseOptions.save(); + } + + onStoppedDragging(); + } + + protected void applyForcedPose() { + if (forcedAnchor == null) { + return; + } + + VRPlayerPoseClient renderPose = VisorAPI.client().getVRLocalPlayer().getPoseData(PlayerPoseType.RENDER); + VRPose anchorPose = forcedAnchor.getSupplier().apply(renderPose); + + Vector3f positionOffset = new Vector3f(dragPositionOffset) + .mul(renderPose.getWorldScale()); + Vector3f newPosition = anchorPose.getCustomVector(positionOffset) + .add(anchorPose.getPosition()); + Matrix4f newRotation = new Matrix4f(anchorPose.getRotation()) + .mul(dragRotationMatrix, new Matrix4f()); + + getPose().update( + newPosition, + newRotation, + getPose().getScale() + ); + } + + public boolean canDragMouse(){ + return mouseDragDelay < System.currentTimeMillis(); + } + public void startDragMouse(){ + mouseDragDelay = System.currentTimeMillis() + 100L; + } + public void finishDragMouse(){ + mouseDragDelay = Long.MAX_VALUE; + } + @Override public void updateCursorData(boolean activeCursor, float rawX, float rawY) { if (!enabled) return; @@ -367,31 +449,6 @@ public boolean isBeingDragged() { return beingDragged; } - @Override - public void setBeingDragged(boolean dragging) { - this.beingDragged = dragging; - } - - @Override - public @NotNull Vector3f getDragPositionOffset() { - return new Vector3f(dragPositionOffset); - } - - @Override - public void setDragPositionOffset(@NotNull Vector3fc offset) { - this.dragPositionOffset.set(offset); - } - - @Override - public @NotNull Matrix4f getDragRotationMatrix() { - return new Matrix4f(dragRotationMatrix); - } - - @Override - public void setDragRotationMatrix(@NotNull Matrix4fc rotationMatrix) { - this.dragRotationMatrix.set(rotationMatrix); - } - public boolean isVisible() { return visible && enabled; @@ -413,7 +470,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int buttonType) { } @Override public boolean mouseReleased(double mouseX, double mouseY, int buttonType) { - if (isBeingDragged() && buttonType == 0) { + if (buttonType == 0 && isBeingDragged()) { stopDragging(); return true; } diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java index f880617e..66aa813b 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/screen/VROverlayScreenInScreen.java @@ -61,7 +61,8 @@ protected void onRender(GuiGraphics guiGraphics, @Override public boolean mouseClicked(double mouseX, double mouseY, int buttonType) { - if (super.mouseClicked(mouseX, mouseY, buttonType)) { + if (buttonType == 0 && isCursorOnDragHandle(getRawMouseX(), getRawMouseY())) { + startDragging(); return true; } if(screen==null) return true; @@ -70,7 +71,8 @@ public boolean mouseClicked(double mouseX, double mouseY, int buttonType) { @Override public boolean mouseReleased(double mouseX, double mouseY, int buttonType) { - if (super.mouseReleased(mouseX, mouseY, buttonType)) { + if (buttonType == 0 && isBeingDragged()) { + stopDragging(); return true; } if(screen==null) return true; diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java index f3fc5975..b562ff5e 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java @@ -217,7 +217,7 @@ public void renderDepthOverlays(float partialTicks, overlay.getPose().getScale() ); - if(overlay.isDraggable()) { + if(overlay.supportsDragging()) { RenderGuiHelper.renderDragHandle( overlay, poseStack, @@ -270,7 +270,7 @@ public void renderHudOverlays(float partialTicks, overlay.supportsLight(), overlay.getPose().getScale() ); - if(overlay.isDraggable()) { + if(overlay.supportsDragging()) { RenderGuiHelper.renderDragHandle( overlay, poseStack, diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java index 92062ce3..9f0bac5a 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java @@ -36,6 +36,8 @@ public class VROverlayGameScreen extends VROverlayFrameBuffer { private float overlayScale = 1.0f; + + private boolean inMainMenu = true; public VROverlayGameScreen(@NotNull VisorAddon owner, @NotNull String id) { super( @@ -109,9 +111,9 @@ public void onScreenChanged(Screen previousGuiScreen, private void orient(Screen previousGuiScreen, Screen newScreen){ - boolean mainMenu = (MC.gameRenderer == null + inMainMenu = (MC.gameRenderer == null || willBeInMenuRoom(newScreen)); - if (mainMenu) { + if (inMainMenu) { orientMainMenu(); return; } @@ -265,7 +267,7 @@ public boolean willBeInMenuRoom(Screen newScreen) { } @Override - public void onDragStopped() { + public void onStoppedDragging() { var relativePose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE); relativePosition = relativePose.convertPositionFrom( PlayerPoseType.RENDER, @@ -328,7 +330,7 @@ public boolean supportsCursor() { } @Override - public boolean isDraggable() { - return true; + public boolean supportsDragging() { + return !inMainMenu; } } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java index 60d39159..35cbb4b1 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java @@ -128,7 +128,7 @@ public void onUpdatePose(float partialTicks) { } @Override - public void onDragStopped() { + public void onStoppedDragging() { var relativePose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE); relativePosition = relativePose.convertPositionFrom( PlayerPoseType.RENDER, @@ -146,7 +146,7 @@ public boolean supportsTwoCursors() { } @Override - public boolean isDraggable() { + public boolean supportsDragging() { return true; } From 7376a9662a1c9216ee4606963b3d5d91c3a481c9 Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:38:20 +0400 Subject: [PATCH 09/15] improved drag handle design --- .../core/client/gui/VROverlayManagerImpl.java | 25 ++++++----- .../render/helpers/RenderGuiHelper.java | 45 ++++++++++++------- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java index b562ff5e..9ac4d2ef 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java @@ -16,6 +16,7 @@ import org.vmstudio.visor.api.client.gui.overlays.options.OptionsScreen; import org.vmstudio.visor.api.client.gui.overlays.framework.VROverlayFrameBuffer; import org.vmstudio.visor.api.client.gui.overlays.framework.VROverlayScreen; +import org.vmstudio.visor.api.common.HandType; import org.vmstudio.visor.core.client.ClientContext; import org.vmstudio.visor.core.client.gui.registry.VROverlayRegistry; import org.vmstudio.visor.core.client.gui.registry.VROverlayTemplateRegistry; @@ -217,13 +218,14 @@ public void renderDepthOverlays(float partialTicks, overlay.getPose().getScale() ); - if(overlay.supportsDragging()) { + boolean hovered = ClientContext.cursorHandler != null + && (ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay + || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay); + + if(overlay.isBeingDragged() || hovered) { RenderGuiHelper.renderDragHandle( overlay, - poseStack, - overlay.getPose().getPosition(), - overlay.getPose().getRotation(), - overlay.getPose().getScale() + poseStack ); } GLUtils.checkGLError("post depth VROverlay quad: " + overlay.getId()); @@ -270,13 +272,16 @@ public void renderHudOverlays(float partialTicks, overlay.supportsLight(), overlay.getPose().getScale() ); - if(overlay.supportsDragging()) { + + + boolean hovered = ClientContext.cursorHandler != null + && (ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay + || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay); + + if(overlay.isBeingDragged() || hovered) { RenderGuiHelper.renderDragHandle( overlay, - poseStack, - overlay.getPose().getPosition(), - overlay.getPose().getRotation(), - overlay.getPose().getScale() + poseStack ); } GLUtils.checkGLError("post hud VROverlay quad: " + overlay.getId()); diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java index 8836cc5a..6ce73dc8 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java @@ -158,15 +158,20 @@ public static void renderOverlayQuad(VROverlay overlay, } - // todo: redesign - public static void renderDragHandle(VROverlay overlay, PoseStack poseStack, Vector3fc position, Matrix4fc orientation, float scale) { + + public static void renderDragHandle(VROverlay overlay, PoseStack poseStack) { + var position = overlay.getPose().getPosition(); + var rotation = overlay.getPose().getRotation(); + var scale = overlay.getPose().getScale(); + boolean dragging = overlay.isBeingDragged(); + VRPlayerPoseClient renderPose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RENDER); var eye = RenderPoseHelper.getCameraPosition(VRRenderState.getRenderPass(), renderPose); float finalScale = scale * renderPose.getWorldScale(); - AtumColor color = overlay.isBeingDragged() - ? AtumColor.immutable(150, 150, 150, 255) - : AtumColor.immutable(100, 100, 100, 255); + AtumColor barColor = dragging + ? AtumColor.immutable(220, 220, 220, 230) + : AtumColor.immutable(190, 190, 190, 150); RenderSystem.disableCull(); RenderSystem.setShader(GameRenderer::getPositionColorShader); @@ -176,25 +181,33 @@ public static void renderDragHandle(VROverlay overlay, PoseStack poseStack, Vect poseStack.pushPose(); poseStack.translate(position.x() - eye.x(), position.y() - eye.y(), position.z() - eye.z()); - poseStack.mulPoseMatrix((Matrix4f) orientation); + poseStack.mulPoseMatrix((Matrix4f) rotation); poseStack.scale(finalScale, finalScale, finalScale); float aspect = overlay.getAspectRatio(); - float halfSize = VROverlayPose.QUAD_SCALE * 0.5f; - float halfHeight = halfSize * aspect; - - float handleTop = -halfHeight; - float handleBottom = -halfHeight - (halfHeight * 0.3f); + float halfWidth = VROverlayPose.QUAD_SCALE * 0.5f; + float halfHeight = halfWidth * aspect; - float r = color.getRed(), g = color.getGreen(), b = color.getBlue(), a = color.getAlpha(); + // Center bar just below the panel. + float barHalfWidth = halfWidth * 0.18f; + float barHalfHeight = halfHeight * 0.025f; + float barGap = halfHeight * 0.04f; + float barCenterY = -halfHeight - barGap - barHalfHeight; + var pose = poseStack.last().pose(); BufferBuilder buf = Tesselator.getInstance().getBuilder(); buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - buf.vertex(poseStack.last().pose(), -halfSize, handleBottom, 0f).color(r, g, b, a).endVertex(); - buf.vertex(poseStack.last().pose(), halfSize, handleBottom, 0f).color(r, g, b, a).endVertex(); - buf.vertex(poseStack.last().pose(), halfSize, handleTop, 0f).color(r, g, b, a).endVertex(); - buf.vertex(poseStack.last().pose(), -halfSize, handleTop, 0f).color(r, g, b, a).endVertex(); + // Bar + float r = barColor.getRed(), g = barColor.getGreen(), b = barColor.getBlue(), a = barColor.getAlpha(); + float left = -barHalfWidth; + float right = barHalfWidth; + float top = barCenterY + barHalfHeight; + float bottom = barCenterY - barHalfHeight; + buf.vertex(pose, left, bottom, 0f).color(r, g, b, a).endVertex(); + buf.vertex(pose, right, bottom, 0f).color(r, g, b, a).endVertex(); + buf.vertex(pose, right, top, 0f).color(r, g, b, a).endVertex(); + buf.vertex(pose, left, top, 0f).color(r, g, b, a).endVertex(); BufferUploader.drawWithShader(buf.end()); From 2f58f14d47830fdbdb8b49f112ba72e55da525c4 Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:54:33 +0400 Subject: [PATCH 10/15] small optimization to not check for focused state when dragging is not supported for overlay --- .../core/client/gui/VROverlayManagerImpl.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java index 9ac4d2ef..74280388 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java @@ -218,11 +218,10 @@ public void renderDepthOverlays(float partialTicks, overlay.getPose().getScale() ); - boolean hovered = ClientContext.cursorHandler != null - && (ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay - || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay); - - if(overlay.isBeingDragged() || hovered) { + if(overlay.supportsDragging() && + (overlay.isBeingDragged() || + ((ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay + || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay)))) { RenderGuiHelper.renderDragHandle( overlay, poseStack @@ -274,11 +273,10 @@ public void renderHudOverlays(float partialTicks, ); - boolean hovered = ClientContext.cursorHandler != null - && (ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay - || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay); - - if(overlay.isBeingDragged() || hovered) { + if(overlay.supportsDragging() && + (overlay.isBeingDragged() || + ((ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay + || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay)))) { RenderGuiHelper.renderDragHandle( overlay, poseStack From 1726e507fdb36e8d77ca40fd19b1207c46c42715 Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Mon, 27 Apr 2026 20:13:14 +0400 Subject: [PATCH 11/15] switched positioning between Calibrate Height and Keyboard in game menu --- .../core/client/gui/screens/GameMenuScreen.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java index 0884a162..bdab399d 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java @@ -102,18 +102,18 @@ private int buildContent(int cx, int y) { case QUICK_ACTIONS -> { addRenderableWidget(makeHalfBtn("Inventory", left, y, b -> this.minecraft.setScreen(new InventoryScreen(this.minecraft.player)))); - addRenderableWidget(makeHalfBtn("Chat", right, y, - b -> this.minecraft.setScreen(new ChatScreen("")))); - y += BTN_H + GAP; - - addRenderableWidget(makeHalfBtn("Keyboard", left, y, b -> - ClientContext.overlayManager.getKeyboardAccessor().setVisible(true))); addRenderableWidget(makeHalfBtn("Calibrate Height", right, y, b -> { VRClientSettings.calibrateHeight(); ClientContext.settingsManager.saveOptions(); })); y += BTN_H + GAP; + addRenderableWidget(makeHalfBtn("Keyboard", left, y, b -> + ClientContext.overlayManager.getKeyboardAccessor().setVisible(true))); + addRenderableWidget(makeHalfBtn("Chat", right, y, + b -> this.minecraft.setScreen(new ChatScreen("")))); + y += BTN_H + GAP; + addRenderableWidget(makeHalfBtn("Vanilla Pause Menu", left, y, b -> this.minecraft.setScreen(new PauseScreen(true)))); addRenderableWidget(makeHalfBtn("VR Settings", right, y, From d17d855065d71147414c9c04f51c68c59caf765e Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Mon, 27 Apr 2026 20:15:08 +0400 Subject: [PATCH 12/15] added cursorBounds support for dragging feature in overlays. Fixed drag handle rendering not aligned with hud rendering depth type --- .../api/client/gui/overlays/VROverlay.java | 23 ++++++++- .../core/client/gui/VROverlayManagerImpl.java | 6 ++- .../render/helpers/RenderGuiHelper.java | 49 ++++++++++++++++--- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java index 59e97e94..47bdb6d1 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/VROverlay.java @@ -291,8 +291,27 @@ default boolean isBeingDragged() { * @return true/false */ default boolean isCursorOnDragHandle(float rawX, float rawY) { - return supportsDragging() - && rawX >= 0f && rawX <= 1f + if (!supportsDragging()) { + return false; + } + int edgeX = getCursorBoundsX(); + int edgeY = getCursorBoundsY(); + int edgeWidth = getCursorBoundsWidth(); + int edgeHeight = getCursorBoundsHeight(); + int width = getWidth(); + int height = getHeight(); + //If cursorBounds are set + if (width > 0 && height > 0 + && edgeX >= 0 && edgeY >= 0 + && edgeWidth >= 0 && edgeHeight >= 0) { + float rawLeft = (float) edgeX / width; + float rawRight = (float) (edgeX + edgeWidth) / width; + float rawTop = (float) (edgeY + edgeHeight) / height; + float rawBottom = rawTop + 0.15f; + return rawX >= rawLeft && rawX <= rawRight + && rawY > rawTop && rawY <= rawBottom; + } + return rawX >= 0f && rawX <= 1f && rawY > 1.0f && rawY <= 1.15f; } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java index 74280388..d16f7c68 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java @@ -224,7 +224,8 @@ public void renderDepthOverlays(float partialTicks, || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay)))) { RenderGuiHelper.renderDragHandle( overlay, - poseStack + poseStack, + false ); } GLUtils.checkGLError("post depth VROverlay quad: " + overlay.getId()); @@ -279,7 +280,8 @@ public void renderHudOverlays(float partialTicks, || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay)))) { RenderGuiHelper.renderDragHandle( overlay, - poseStack + poseStack, + true ); } GLUtils.checkGLError("post hud VROverlay quad: " + overlay.getId()); diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java index 6ce73dc8..353d23ee 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java @@ -159,7 +159,9 @@ public static void renderOverlayQuad(VROverlay overlay, } - public static void renderDragHandle(VROverlay overlay, PoseStack poseStack) { + public static void renderDragHandle(VROverlay overlay, + PoseStack poseStack, + boolean depthAlways) { var position = overlay.getPose().getPosition(); var rotation = overlay.getPose().getRotation(); var scale = overlay.getPose().getScale(); @@ -177,7 +179,15 @@ public static void renderDragHandle(VROverlay overlay, PoseStack poseStack) { RenderSystem.setShader(GameRenderer::getPositionColorShader); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); - RenderSystem.depthMask(true); + + if (depthAlways) { + RenderSystem.depthFunc(GL11C.GL_ALWAYS); + RenderSystem.depthMask(false); + } else { + RenderSystem.depthFunc(GL11C.GL_LEQUAL); + RenderSystem.depthMask(true); + } + RenderSystem.enableDepthTest(); poseStack.pushPose(); poseStack.translate(position.x() - eye.x(), position.y() - eye.y(), position.z() - eye.z()); @@ -188,20 +198,37 @@ public static void renderDragHandle(VROverlay overlay, PoseStack poseStack) { float halfWidth = VROverlayPose.QUAD_SCALE * 0.5f; float halfHeight = halfWidth * aspect; - // Center bar just below the panel. - float barHalfWidth = halfWidth * 0.18f; + float barCenterX = 0f; + float barHalfWidth = halfWidth * 0.18f; + float regionBottom = -halfHeight; + + int edgeX = overlay.getCursorBoundsX(); + int edgeY = overlay.getCursorBoundsY(); + int edgeWidth = overlay.getCursorBoundsWidth(); + int edgeHeight = overlay.getCursorBoundsHeight(); + int width = overlay.getWidth(); + int height = overlay.getHeight(); + if (width > 0 && height > 0 + && edgeX >= 0 && edgeY >= 0 + && edgeWidth >= 0 && edgeHeight >= 0) { + float nx0 = -halfWidth + ((float) edgeX / width) * (2f * halfWidth); + float nx1 = -halfWidth + ((float) (edgeX + edgeWidth) / width) * (2f * halfWidth); + regionBottom = halfHeight - ((float) (edgeY + edgeHeight) / height) * (2f * halfHeight); + barCenterX = (nx0 + nx1) * 0.5f; + barHalfWidth = (nx1 - nx0) * 0.18f; + } + float barHalfHeight = halfHeight * 0.025f; float barGap = halfHeight * 0.04f; - float barCenterY = -halfHeight - barGap - barHalfHeight; + float barCenterY = regionBottom - barGap - barHalfHeight; var pose = poseStack.last().pose(); BufferBuilder buf = Tesselator.getInstance().getBuilder(); buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - // Bar float r = barColor.getRed(), g = barColor.getGreen(), b = barColor.getBlue(), a = barColor.getAlpha(); - float left = -barHalfWidth; - float right = barHalfWidth; + float left = barCenterX - barHalfWidth; + float right = barCenterX + barHalfWidth; float top = barCenterY + barHalfHeight; float bottom = barCenterY - barHalfHeight; buf.vertex(pose, left, bottom, 0f).color(r, g, b, a).endVertex(); @@ -211,7 +238,13 @@ public static void renderDragHandle(VROverlay overlay, PoseStack poseStack) { BufferUploader.drawWithShader(buf.end()); + // Restore + RenderSystem.depthFunc(GL11C.GL_LEQUAL); + RenderSystem.depthMask(true); + RenderSystem.enableDepthTest(); + RenderSystem.defaultBlendFunc(); RenderSystem.enableCull(); + poseStack.popPose(); } From 2b986104688765a628d38a2bf2c27558884ee859 Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Mon, 27 Apr 2026 20:15:18 +0400 Subject: [PATCH 13/15] added cursor bounds for keyboard --- .../builtin/keyboard/VROverlayKeyboard.java | 8 +++++ .../client/gui/screens/VRKeyboardScreen.java | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java index 35cbb4b1..ed660a24 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java @@ -63,6 +63,14 @@ public VROverlayKeyboard(@NotNull VisorAddon owner, VisorAPI.eventBus().registerListener(owner,this); } + @Override + protected void init() { + super.init(); + cursorBoundsX = getScreen().getCursorBoundsX(); + cursorBoundsY = getScreen().getCursorBoundsY(); + cursorBoundsWidth = getScreen().getCursorBoundsWidth(); + cursorBoundsHeight = getScreen().getCursorBoundsHeight(); + } @VREventHandler public void disableWorldHands(AllowClientFeatureVREvent event){ diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java index 4fb01b0d..52c55ff5 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/VRKeyboardScreen.java @@ -4,6 +4,7 @@ import lombok.Setter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import org.lwjgl.glfw.GLFW; @@ -27,6 +28,15 @@ public class VRKeyboardScreen extends Screen { @Setter private int pressTick; + @Getter + private int cursorBoundsX = -1; + @Getter + private int cursorBoundsY = -1; + @Getter + private int cursorBoundsWidth = -1; + @Getter + private int cursorBoundsHeight = -1; + public VRKeyboardScreen(Component component) { super(component); } @@ -241,6 +251,30 @@ public void init() { .usePressTask(false) .build() ); + //CURSOR BOUNDS + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + for (var child : this.children()) { + if (child instanceof AbstractWidget widget) { + minX = Math.min(minX, widget.getX()); + minY = Math.min(minY, widget.getY()); + maxX = Math.max(maxX, widget.getX() + widget.getWidth()); + maxY = Math.max(maxY, widget.getY() + widget.getHeight()); + } + } + if (minX == Integer.MAX_VALUE) { + cursorBoundsX = -1; + cursorBoundsY = -1; + cursorBoundsWidth = -1; + cursorBoundsHeight = -1; + } else { + cursorBoundsX = minX; + cursorBoundsY = minY; + cursorBoundsWidth = maxX - minX; + cursorBoundsHeight = maxY - minY; + } } private void pressKeyboardKey(KeyboardKey key) { From 6c8d7e322906572e43d5651f95009e7f9140afa1 Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Mon, 27 Apr 2026 22:31:12 +0400 Subject: [PATCH 14/15] gameMenu improvements --- .../client/gui/screens/GameMenuScreen.java | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java index bdab399d..6dc7b151 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/screens/GameMenuScreen.java @@ -29,9 +29,9 @@ public class GameMenuScreen extends Screen { private static final int SECTION_GAP = 6; private enum Tab{ - QUICK_ACTIONS(Component.literal("Quick Actions")), - WORLD_AND_MODES(Component.literal("World & Modes")), - TOOLS(Component.literal("F3 Tools")); + MAIN(Component.literal("Main")), + COMMANDS(Component.literal("Commands")), + TOOLS(Component.literal("Tools")); Component label; @@ -40,7 +40,7 @@ private enum Tab{ } } - private Tab currentTab = Tab.QUICK_ACTIONS; + private Tab currentTab = Tab.MAIN; private final List sectionHeaderPos = new ArrayList<>(); // {x, y} private final List sectionHeaderTexts = new ArrayList<>(); @@ -53,11 +53,10 @@ public GameMenuScreen() { @Override protected void init() { TaskHotBar.setResetData(true); - ClientContext.overlayManager.getKeyboardAccessor().setVisible(false); boolean hasPerms = this.minecraft.player != null && this.minecraft.player.hasPermissions(2); - if (this.currentTab == Tab.WORLD_AND_MODES && !hasPerms) { - this.currentTab = Tab.QUICK_ACTIONS; + if (this.currentTab == Tab.COMMANDS && !hasPerms) { + this.currentTab = Tab.MAIN; } sectionHeaderPos.clear(); @@ -75,9 +74,10 @@ protected void init() { this.rebuildWidgets(); }).pos(tx + tab.ordinal() * (TAB_W + 4), y).width(TAB_W).build(); - if (t == Tab.WORLD_AND_MODES && !hasPerms) { - btn.active = false; + if (t == Tab.COMMANDS && !hasPerms) { + btn.visible = false; } else { + btn.visible = true; btn.active = (this.currentTab != tab); } addRenderableWidget(btn); @@ -99,7 +99,7 @@ private int buildContent(int cx, int y) { int right = left + BTN_HALF + GAP; switch (this.currentTab) { - case QUICK_ACTIONS -> { + case MAIN -> { addRenderableWidget(makeHalfBtn("Inventory", left, y, b -> this.minecraft.setScreen(new InventoryScreen(this.minecraft.player)))); addRenderableWidget(makeHalfBtn("Calibrate Height", right, y, b -> { @@ -114,14 +114,14 @@ private int buildContent(int cx, int y) { b -> this.minecraft.setScreen(new ChatScreen("")))); y += BTN_H + GAP; - addRenderableWidget(makeHalfBtn("Vanilla Pause Menu", left, y, + addRenderableWidget(makeHalfBtn("Pause Menu", left, y, b -> this.minecraft.setScreen(new PauseScreen(true)))); addRenderableWidget(makeHalfBtn("VR Settings", right, y, b -> this.minecraft.setScreen(new VRSettingsScreen(this)))); y += BTN_H + GAP; } - case WORLD_AND_MODES -> { + case COMMANDS -> { registerSection(left, y, "GAME MODE"); y += LABEL_H; @@ -205,7 +205,7 @@ public void render(GuiGraphics gfx, int mouseX, int mouseY, float delta) { int accentX = cx - COLUMN_W / 2 + currentTab.ordinal() * (TAB_W + 4); gfx.fill(accentX, tabStripY - 2, accentX + TAB_W, tabStripY - 1, 0xFF55FF55); - if (this.currentTab == Tab.WORLD_AND_MODES) { + if (this.currentTab == Tab.COMMANDS) { for (int i = 0; i < sectionHeaderPos.size(); i++) { int sx = sectionHeaderPos.get(i)[0]; int sy = sectionHeaderPos.get(i)[1]; @@ -225,9 +225,9 @@ public void render(GuiGraphics gfx, int mouseX, int mouseY, float delta) { private int totalColumnHeight() { int contentH = switch (this.currentTab) { - case QUICK_ACTIONS -> 3 * (BTN_H + GAP); + case MAIN -> 3 * (BTN_H + GAP); case TOOLS -> 2 * (BTN_H + GAP); - case WORLD_AND_MODES -> 3 * LABEL_H + 4 * (BTN_H + GAP) + 2 * SECTION_GAP; + case COMMANDS -> 3 * LABEL_H + 4 * (BTN_H + GAP) + 2 * SECTION_GAP; }; return 9 + 16 + BTN_H + 6 + contentH + 6 + BTN_H; } @@ -275,8 +275,4 @@ private void onDisconnect() { } - @Override - public boolean isPauseScreen() { - return false; - } } From 7489640b719ea63624ca74a3f84f57d7473f18d1 Mon Sep 17 00:00:00 2001 From: Bogdan <53151806+Phoenix-Ra@users.noreply.github.com> Date: Tue, 28 Apr 2026 01:07:18 +0400 Subject: [PATCH 15/15] reworked keyboard logic from "force positioning and removing" to "player decide the positioning and removal" --- .../api/client/gui/VRKeyboardAccessor.java | 2 +- .../overlays/framework/VROverlayScreen.java | 2 +- .../core/client/gui/VROverlayManagerImpl.java | 33 ++---- .../overlays/builtin/VROverlayGameScreen.java | 4 +- .../builtin/keyboard/VROverlayKeyboard.java | 35 +++--- .../client/input/mouse/MouseClickHandler.java | 6 + .../render/helpers/RenderGuiHelper.java | 103 +++++++----------- 7 files changed, 80 insertions(+), 105 deletions(-) diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/VRKeyboardAccessor.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/VRKeyboardAccessor.java index dac07fe2..d476d383 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/VRKeyboardAccessor.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/VRKeyboardAccessor.java @@ -26,7 +26,7 @@ public interface VRKeyboardAccessor { * * @param attachTo the screen that has to be attached to keyboard */ - void showKeyboard(@NotNull Screen attachTo); + void showKeyboard(@Nullable Screen attachTo); /** * The screen, keyboard is attached to diff --git a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java index 978c8dfc..5ec0edaa 100644 --- a/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java +++ b/visor-api/src/main/java/org/vmstudio/visor/api/client/gui/overlays/framework/VROverlayScreen.java @@ -286,7 +286,7 @@ public void setEnabled(boolean flag) { .getOverlayManager() .getKeyboardAccessor(); if (keyboardAccessor.getAttachedTo() == this) { - keyboardAccessor.setVisible(false); + keyboardAccessor.showKeyboard(null); } onDisable(); } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java index d16f7c68..6ef289fa 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/VROverlayManagerImpl.java @@ -208,6 +208,11 @@ public void renderDepthOverlays(float partialTicks, throw new RuntimeException("Tried to render overlay quad with null renderTarget: " + overlay.getId()); } + boolean drawDragHandle = overlay.supportsDragging() && + (overlay.isBeingDragged() || + ((ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay + || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay))); + RenderGuiHelper.renderOverlayQuad( overlay, poseStack, @@ -215,19 +220,9 @@ public void renderDepthOverlays(float partialTicks, overlay.getPose().getRotation(), false, // depthAlways = false, use GL_LEQUAL overlay.supportsLight(), + drawDragHandle, overlay.getPose().getScale() ); - - if(overlay.supportsDragging() && - (overlay.isBeingDragged() || - ((ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay - || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay)))) { - RenderGuiHelper.renderDragHandle( - overlay, - poseStack, - false - ); - } GLUtils.checkGLError("post depth VROverlay quad: " + overlay.getId()); } @@ -263,6 +258,10 @@ public void renderHudOverlays(float partialTicks, throw new RuntimeException("Tried to render overlay quad with null renderTarget: " + overlay.getId()); } + boolean drawDragHandle = overlay.supportsDragging() && + (overlay.isBeingDragged() || + ((ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay + || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay))); RenderGuiHelper.renderOverlayQuad( overlay, poseStack, @@ -270,20 +269,10 @@ public void renderHudOverlays(float partialTicks, overlay.getPose().getRotation(), true, // depthAlways = true, use GL_ALWAYS overlay.supportsLight(), + drawDragHandle, overlay.getPose().getScale() ); - - if(overlay.supportsDragging() && - (overlay.isBeingDragged() || - ((ClientContext.cursorHandler.getFocusedOverlay(HandType.MAIN) == overlay - || ClientContext.cursorHandler.getFocusedOverlay(HandType.OFFHAND) == overlay)))) { - RenderGuiHelper.renderDragHandle( - overlay, - poseStack, - true - ); - } GLUtils.checkGLError("post hud VROverlay quad: " + overlay.getId()); } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java index 9f0bac5a..dfb0bd8c 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/VROverlayGameScreen.java @@ -96,7 +96,7 @@ public void onScreenChanged(Screen previousGuiScreen, Screen attachedTo = keyboardAccessor.getAttachedTo(); if (attachedTo != null && attachedTo == previousGuiScreen) { - keyboardAccessor.setVisible(false); + keyboardAccessor.showKeyboard(null); } } else if (newScreen instanceof ChatScreen) { if(!keyboardAccessor.isVisible() @@ -159,8 +159,6 @@ private void orient(Screen previousGuiScreen, } - ClientContext.overlayManager.getKeyboardAccessor() - .resetPose(); } private void orientMainMenu(){ diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java index ed660a24..3e5ac232 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/gui/overlays/builtin/keyboard/VROverlayKeyboard.java @@ -116,7 +116,7 @@ public void setVisible(boolean flag){ } @Override - public void showKeyboard(@NotNull Screen attachTo) { + public void showKeyboard(@Nullable Screen attachTo) { setVisible(true, attachTo); } @@ -161,10 +161,11 @@ public boolean supportsDragging() { public void setVisible(boolean flag, @Nullable Screen attachedTo) { + boolean changePose = flag != shown; shown = flag; if (shown) { - orient(attachedTo); + orient(attachedTo, changePose); shiftPressed = false; activeLayoutId = getEnabledLayoutIds().get(0); initAgain = true; @@ -213,7 +214,7 @@ public boolean hasMultipleLayouts() { return getEnabledLayoutIds().size() > 1; } - private void orient(@Nullable Screen attachedTo) { + private void orient(@Nullable Screen attachedTo, boolean changePose) { if (!shown) { this.attachedTo = null; return; @@ -221,19 +222,21 @@ private void orient(@Nullable Screen attachedTo) { this.attachedTo = attachedTo; - VROverlayHelper.applyPose( - this, - PoseAnchor.HMD, - PoseAnchor.HMD, - getPose().getScale(), - true, - posOffset, - rotationOffset - ); - relativePosition = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE) - .convertPositionFrom(PlayerPoseType.RENDER, getPose().getPosition()); - relativeRotation = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE) - .convertRotationFrom(PlayerPoseType.RENDER, getPose().getRotation()); + if(changePose) { + VROverlayHelper.applyPose( + this, + PoseAnchor.HMD, + PoseAnchor.HMD, + getPose().getScale(), + true, + posOffset, + rotationOffset + ); + relativePosition = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE) + .convertPositionFrom(PlayerPoseType.RENDER, getPose().getPosition()); + relativeRotation = ClientContext.localPlayer.getPoseData(PlayerPoseType.RELATIVE) + .convertRotationFrom(PlayerPoseType.RENDER, getPose().getRotation()); + } } diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/input/mouse/MouseClickHandler.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/input/mouse/MouseClickHandler.java index 3bb11e8c..71d98b5a 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/input/mouse/MouseClickHandler.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/input/mouse/MouseClickHandler.java @@ -263,6 +263,12 @@ private void processScreen() { } private void processGame(@NotNull HandType handType) { + //disable keyboard (clicked outside it and not with screen opened) + var keyboardAccessor = ClientContext.overlayManager.getKeyboardAccessor(); + if(keyboardAccessor.isVisible()){ + keyboardAccessor.setVisible(false); + return; + } // update active hand if only one hand is pressed var activeHand = ClientContext.localPlayer.getActiveHand(); if (activeHand != handType) { diff --git a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java index 353d23ee..60420822 100644 --- a/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java +++ b/visor-core/src/main/java/org/vmstudio/visor/core/client/render/helpers/RenderGuiHelper.java @@ -44,9 +44,9 @@ public static void renderOverlayQuad(VROverlay overlay, Matrix4fc orientation, boolean depthAlways, boolean useLight, + boolean drawDragHandle, float scale ) { - // --- Prepare variables --- VRPlayerPoseClient renderPose = ClientContext.localPlayer .getPoseData(PlayerPoseType.RENDER); @@ -56,27 +56,32 @@ public static void renderOverlayQuad(VROverlay overlay, ); scale = scale * renderPose.getWorldScale(); - float fogStartCache = RenderSystem.getShaderFogStart(); var color = AtumColor.WHITE.asMutable(); - // --- Setup GL --- + boolean dragging = overlay.isBeingDragged(); + var barColor = (dragging + ? AtumColor.immutable(220, 220, 220, 230) + : AtumColor.immutable(190, 190, 190, 150)).asMutable(); + var renderTarget = overlay.getRenderTarget(); assert renderTarget != null; - renderTarget.bindRead(); RenderSystem.disableCull(); RenderSystem.setShaderTexture(0, renderTarget.getColorTextureId()); if (!VRRenderState.isInMainMenu()) { - RenderSystem.setShaderFogStart(Float.MAX_VALUE); if (MC.player != null && MC.player.isShiftKeyDown()) { color.setRGBA( color.getRed(), color.getGreen(), color.getBlue(), - color.getAlpha() * 0.75F + (int) (color.getAlpha() * 0.75F) + ); + barColor.setRGBA( + barColor.getRed(), barColor.getGreen(), barColor.getBlue(), + (int) (barColor.getAlpha() * 0.75F) ); } @@ -97,33 +102,34 @@ public static void renderOverlayQuad(VROverlay overlay, if (depthAlways) { RenderSystem.depthFunc(GL11C.GL_ALWAYS); - //disable mask to not mess up depth for something RenderSystem.depthMask(false); } else { RenderSystem.depthFunc(GL11C.GL_LEQUAL); RenderSystem.depthMask(true); } - RenderSystem.enableDepthTest(); - // --- Setup Pose --- + // --- Pose --- poseStack.pushPose(); poseStack.translate(position.x() - eye.x(), position.y() - eye.y(), position.z() - eye.z()); poseStack.mulPoseMatrix((Matrix4f) orientation); poseStack.scale(scale, scale, scale); - - // --- Render --- + // --- Quad + light --- + int packedLight = -1; if (MC.level != null && useLight) { + Vector3fc lightPos = position; if (RenderHelper.isInSolidBlock(position) || ((GameRendererExtension) MC.gameRenderer).visor$isInBlock()) { - position = ClientContext.localPlayer.getPoseData(PlayerPoseType.RENDER).getHmd().getPosition(); + lightPos = ClientContext.localPlayer + .getPoseData(PlayerPoseType.RENDER) + .getHmd() + .getPosition(); } - int minLight = ShadersHelper.shaderLight(); - int light = ClientUtils.getCombinedLightWithMin( + packedLight = ClientUtils.getCombinedLightWithMin( MC.level, - BlockPos.containing(new Vec3((Vector3f) position)), + BlockPos.containing(new Vec3((Vector3f) lightPos)), minLight ); RenderHelper.renderDisplayQuadWithLight( @@ -132,7 +138,7 @@ public static void renderOverlayQuad(VROverlay overlay, (float) overlay.getWidth(), (float) overlay.getHeight(), VROverlayPose.QUAD_SCALE, - light, + packedLight, false ); } else { @@ -145,6 +151,17 @@ public static void renderOverlayQuad(VROverlay overlay, ); } + // --- Drag handle bar + if (drawDragHandle && overlay.supportsDragging()) { + float brightness = 1f; + if (packedLight >= 0) { + int blockLight = (packedLight >> 4) & 0xF; + int skyLight = (packedLight >> 20) & 0xF; + brightness = Math.max(0.2f, Math.max(blockLight, skyLight) / 15f); + } + drawDragHandleBar(overlay, poseStack, barColor, brightness); + } + // --- Restore --- RenderSystem.setShaderFogStart(fogStartCache); RenderSystem.depthFunc(GL11C.GL_LEQUAL); @@ -154,45 +171,13 @@ public static void renderOverlayQuad(VROverlay overlay, RenderSystem.enableCull(); poseStack.popPose(); - - } - - public static void renderDragHandle(VROverlay overlay, - PoseStack poseStack, - boolean depthAlways) { - var position = overlay.getPose().getPosition(); - var rotation = overlay.getPose().getRotation(); - var scale = overlay.getPose().getScale(); - boolean dragging = overlay.isBeingDragged(); - - VRPlayerPoseClient renderPose = ClientContext.localPlayer.getPoseData(PlayerPoseType.RENDER); - var eye = RenderPoseHelper.getCameraPosition(VRRenderState.getRenderPass(), renderPose); - float finalScale = scale * renderPose.getWorldScale(); - - AtumColor barColor = dragging - ? AtumColor.immutable(220, 220, 220, 230) - : AtumColor.immutable(190, 190, 190, 150); - - RenderSystem.disableCull(); + private static void drawDragHandleBar(VROverlay overlay, + PoseStack poseStack, + AtumColor barColor, + float brightness) { RenderSystem.setShader(GameRenderer::getPositionColorShader); - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - - if (depthAlways) { - RenderSystem.depthFunc(GL11C.GL_ALWAYS); - RenderSystem.depthMask(false); - } else { - RenderSystem.depthFunc(GL11C.GL_LEQUAL); - RenderSystem.depthMask(true); - } - RenderSystem.enableDepthTest(); - - poseStack.pushPose(); - poseStack.translate(position.x() - eye.x(), position.y() - eye.y(), position.z() - eye.z()); - poseStack.mulPoseMatrix((Matrix4f) rotation); - poseStack.scale(finalScale, finalScale, finalScale); float aspect = overlay.getAspectRatio(); float halfWidth = VROverlayPose.QUAD_SCALE * 0.5f; @@ -226,7 +211,10 @@ public static void renderDragHandle(VROverlay overlay, BufferBuilder buf = Tesselator.getInstance().getBuilder(); buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - float r = barColor.getRed(), g = barColor.getGreen(), b = barColor.getBlue(), a = barColor.getAlpha(); + float r = barColor.getRed() * brightness; + float g = barColor.getGreen() * brightness; + float b = barColor.getBlue() * brightness; + float a = barColor.getAlpha(); float left = barCenterX - barHalfWidth; float right = barCenterX + barHalfWidth; float top = barCenterY + barHalfHeight; @@ -237,15 +225,6 @@ public static void renderDragHandle(VROverlay overlay, buf.vertex(pose, left, top, 0f).color(r, g, b, a).endVertex(); BufferUploader.drawWithShader(buf.end()); - - // Restore - RenderSystem.depthFunc(GL11C.GL_LEQUAL); - RenderSystem.depthMask(true); - RenderSystem.enableDepthTest(); - RenderSystem.defaultBlendFunc(); - RenderSystem.enableCull(); - - poseStack.popPose(); } }