1
0
forked from Clones/Controlify

joystick axis rendering (no textures), improve config error handling and fix joystick deadzones being unordered

This commit is contained in:
isXander
2023-02-17 00:46:40 +00:00
parent fa1a3331e6
commit 8e31472c07
89 changed files with 169 additions and 104 deletions

View File

@ -125,7 +125,10 @@ public class ControllerBindings<T extends ControllerState> {
public void fromJson(JsonObject json) {
for (var binding : registry().values()) {
var bind = json.get(binding.id().toString()).getAsJsonObject();
if (bind == null) continue;
if (bind == null) {
Controlify.LOGGER.warn("Unknown control: " + binding.id() + " in config file. Skipping!");
continue;
}
binding.setCurrentBind(IBind.fromJson(bind, controller));
}
}

View File

@ -72,7 +72,7 @@ public enum GamepadBind implements IBind<GamepadState> {
public void draw(PoseStack matrices, int x, int centerY, Controller<GamepadState, ?> controller) {
ResourceLocation texture;
if (((GamepadConfig)controller.config()).theme == BuiltinGamepadTheme.DEFAULT) {
texture = new ResourceLocation("controlify", "textures/gui/gamepad_buttons/" + controller.type().identifier() + "/" + identifier + ".png");
texture = new ResourceLocation("controlify", "textures/gui/gamepad/" + controller.type().identifier() + "/" + identifier + ".png");
} else {
texture = textureLocations.get(((GamepadConfig)controller.config()).theme);
}

View File

@ -32,14 +32,10 @@ public interface IBind<S extends ControllerState> {
case JoystickButtonBind.BIND_ID -> JoystickButtonBind.fromJson(json, joystick);
case JoystickHatBind.BIND_ID -> JoystickHatBind.fromJson(json, joystick);
case JoystickAxisBind.BIND_ID -> JoystickAxisBind.fromJson(json, joystick);
default -> {
Controlify.LOGGER.error("Unknown bind type: " + type);
yield new EmptyBind<>();
}
default -> throw new IllegalStateException("Unknown bind type for joystick: " + type);
};
}
Controlify.LOGGER.error("Could not parse bind for controller: " + controller.name());
return new EmptyBind<>();
throw new IllegalStateException("Unknown controller type: " + controller.getClass().getName());
}
}

View File

@ -1,13 +1,18 @@
package dev.isxander.controlify.bindings;
import com.google.gson.JsonObject;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.joystick.JoystickController;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.controller.joystick.mapping.JoystickMapping;
import dev.isxander.controlify.controller.joystick.mapping.UnmappedJoystickMapping;
import dev.isxander.controlify.gui.DrawSize;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.Objects;
@ -35,22 +40,32 @@ public class JoystickAxisBind implements IBind<JoystickState> {
@Override
public void draw(PoseStack matrices, int x, int centerY, Controller<JoystickState, ?> controller) {
var font = Minecraft.getInstance().font;
font.drawShadow(matrices, getTempButtonName(), x + 1.5f, centerY - font.lineHeight / 2f, 0xFFFFFF);
if (controller != joystick) return;
JoystickMapping mapping = joystick.mapping();
String type = joystick.type().identifier();
String axis = mapping.axis(axisIndex).identifier();
String direction = mapping.axis(axisIndex).getDirectionIdentifier(axisIndex, this.direction);
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/axis_" + axis + "_" + direction + ".png");
RenderSystem.setShaderTexture(0, texture);
RenderSystem.setShaderColor(1, 1, 1, 1);
GuiComponent.blit(matrices, x, centerY - 11, 0, 0, 22, 22, 22, 22);
if (mapping instanceof UnmappedJoystickMapping) {
var text = Integer.toString(axisIndex + 1);
var font = Minecraft.getInstance().font;
GuiComponent.drawCenteredString(matrices, font, text, x + 11, centerY - font.lineHeight / 2, 0xFFFFFF);
}
}
@Override
public DrawSize drawSize() {
var font = Minecraft.getInstance().font;
return new DrawSize(font.width(getTempButtonName()) + 3, font.lineHeight);
}
int width = 22;
if (joystick.mapping() instanceof UnmappedJoystickMapping)
width = Math.max(width, Minecraft.getInstance().font.width(Integer.toString(axisIndex + 1)));
private Component getTempButtonName() {
var axis = joystick.mapping().axis(axisIndex);
return Component.empty()
.append(axis.name())
.append(" ")
.append(axis.getDirectionName(axisIndex, direction));
return new DrawSize(width, 22);
}
@Override

View File

@ -1,13 +1,16 @@
package dev.isxander.controlify.bindings;
import com.google.gson.JsonObject;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.joystick.JoystickController;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.gui.DrawSize;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.Objects;
@ -29,19 +32,20 @@ public class JoystickButtonBind implements IBind<JoystickState> {
@Override
public void draw(PoseStack matrices, int x, int centerY, Controller<JoystickState, ?> controller) {
var font = Minecraft.getInstance().font;
if (controller != joystick) return;
font.drawShadow(matrices, getTempButtonName(), x + 1.5f, centerY - font.lineHeight / 2f, 0xFFFFFF);
String type = joystick.type().identifier();
String button = joystick.mapping().button(buttonIndex).identifier();
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/button_" + button + ".png");
RenderSystem.setShaderTexture(0, texture);
RenderSystem.setShaderColor(1, 1, 1, 1);
GuiComponent.blit(matrices, x, centerY - 11, 0, 0, 22, 22, 22, 22);
}
@Override
public DrawSize drawSize() {
var font = Minecraft.getInstance().font;
return new DrawSize(font.width(getTempButtonName()) + 3, font.lineHeight);
}
private Component getTempButtonName() {
return joystick.mapping().button(buttonIndex).name();
return new DrawSize(22, 22);
}
@Override

View File

@ -1,13 +1,16 @@
package dev.isxander.controlify.bindings;
import com.google.gson.JsonObject;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.joystick.JoystickController;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.gui.DrawSize;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.Objects;
@ -31,21 +34,30 @@ public class JoystickHatBind implements IBind<JoystickState> {
@Override
public void draw(PoseStack matrices, int x, int centerY, Controller<JoystickState, ?> controller) {
var font = Minecraft.getInstance().font;
font.drawShadow(matrices, getTempButtonName(), x + 1.5f, centerY - font.lineHeight / 2f, 0xFFFFFF);
if (controller != joystick) return;
String type = joystick.type().identifier();
String button = joystick.mapping().button(hatIndex).identifier();
String direction = "centered";
if (hatState.isUp())
direction = "up";
else if (hatState.isDown())
direction = "down";
else if (hatState.isLeft())
direction = "left";
else if (hatState.isRight())
direction = "right";
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/hat" + button + "_" + direction + ".png");
RenderSystem.setShaderTexture(0, texture);
RenderSystem.setShaderColor(1, 1, 1, 1);
GuiComponent.blit(matrices, x, centerY - 11, 0, 0, 22, 22, 22, 22);
}
@Override
public DrawSize drawSize() {
var font = Minecraft.getInstance().font;
return new DrawSize(font.width(getTempButtonName()) + 3, font.lineHeight);
}
private Component getTempButtonName() {
return Component.empty()
.append(joystick.mapping().hat(hatIndex).name())
.append(" ")
.append(hatState.getDisplayName());
return new DrawSize(22, 22);
}
@Override

View File

@ -105,8 +105,14 @@ public class ControlifyConfig {
}
private void applyControllerConfig(Controller<?, ?> controller, JsonObject object) {
controller.setConfig(GSON, object.getAsJsonObject("config"));
controller.bindings().fromJson(object.getAsJsonObject("bindings"));
try {
controller.setConfig(GSON, object.getAsJsonObject("config"));
controller.bindings().fromJson(object.getAsJsonObject("bindings"));
} catch (Exception e) {
Controlify.LOGGER.error("Failed to load controller data for " + controller.uid() + ". Resetting to default!", e);
controller.resetConfig();
save();
}
}
public GlobalSettings globalSettings() {

View File

@ -27,6 +27,7 @@ import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@ -190,7 +191,8 @@ public class YACLHelper {
.collect(Collectors.toMap(
i -> joystick.mapping().axis(i).identifier(),
i -> i,
(x, y) -> x
(x, y) -> x,
LinkedHashMap::new
))
.values();
var jsCfg = joystick.config();

View File

@ -97,6 +97,11 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
return this.defaultConfig;
}
@Override
public void resetConfig() {
this.config = defaultConfig();
}
@Override
public void setConfig(Gson gson, JsonElement json) {
C newConfig = gson.fromJson(json, new TypeToken<C>(getClass()){}.getType());

View File

@ -5,6 +5,7 @@ import com.google.gson.JsonElement;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.controller.gamepad.GamepadController;
import dev.isxander.controlify.controller.joystick.JoystickController;
import dev.isxander.controlify.debug.DebugProperties;
import org.hid4java.HidDevice;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
@ -24,6 +25,7 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
C config();
C defaultConfig();
void resetConfig();
void setConfig(Gson gson, JsonElement json);
ControllerType type();
@ -39,7 +41,7 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
return CONTROLLERS.get(joystickId);
}
if (GLFW.glfwJoystickIsGamepad(joystickId)) {
if (GLFW.glfwJoystickIsGamepad(joystickId) && !DebugProperties.FORCE_JOYSTICK) {
GamepadController controller = new GamepadController(joystickId, device);
CONTROLLERS.put(joystickId, controller);
return controller;
@ -94,6 +96,11 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
return config;
}
@Override
public void resetConfig() {
}
@Override
public void setConfig(Gson gson, JsonElement json) {

View File

@ -6,17 +6,12 @@ import java.util.HashMap;
import java.util.Map;
public class JoystickConfig extends ControllerConfig {
private final Map<String, Float> deadzones;
private Map<String, Float> deadzones;
private transient JoystickController controller;
public JoystickConfig(JoystickController controller) {
this.controller = controller;
deadzones = new HashMap<>();
for (int i = 0; i < controller.axisCount(); i++) {
if (controller.mapping().axis(i).requiresDeadzone())
deadzones.put(controller.mapping().axis(i).identifier(), 0.2f);
}
setup(controller);
}
@Override
@ -35,7 +30,14 @@ public class JoystickConfig extends ControllerConfig {
return deadzones.getOrDefault(controller.mapping().axis(axis).identifier(), 0.2f);
}
void setController(JoystickController controller) {
void setup(JoystickController controller) {
this.controller = controller;
if (this.deadzones == null) {
deadzones = new HashMap<>();
for (int i = 0; i < controller.axisCount(); i++) {
if (controller.mapping().axis(i).requiresDeadzone())
deadzones.put(controller.mapping().axis(i).identifier(), 0.2f);
}
}
}
}

View File

@ -64,6 +64,6 @@ public class JoystickController extends AbstractController<JoystickState, Joysti
@Override
public void setConfig(Gson gson, JsonElement json) {
super.setConfig(gson, json);
this.config.setController(this);
this.config.setup(this);
}
}

View File

@ -133,9 +133,8 @@ public class DataJoystickMapping implements JoystickMapping {
}
@Override
public Component getDirectionName(int axis, JoystickAxisBind.AxisDirection direction) {
var directionId = axisNames().get(ids.indexOf(axis)).get(direction.ordinal());
return Component.translatable("controlify.joystick_mapping." + typeId() + ".axis." + identifier() + "." + directionId);
public String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction) {
return this.axisNames().get(ids.indexOf(axis)).get(direction.ordinal());
}
}
@ -144,6 +143,8 @@ public class DataJoystickMapping implements JoystickMapping {
public Component name() {
return Component.translatable("controlify.joystick_mapping." + typeId() + ".button." + identifier());
}
}
private record HatMapping(String identifier, String typeId) implements Hat {

View File

@ -21,7 +21,7 @@ public interface JoystickMapping {
boolean isAxisResting(float value);
Component getDirectionName(int axis, JoystickAxisBind.AxisDirection direction);
String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction);
}
interface Button {

View File

@ -24,36 +24,36 @@ public class UnmappedJoystickMapping implements JoystickMapping {
private record UnmappedAxis(int axis) implements Axis {
@Override
public String identifier() {
public String identifier() {
return "axis-" + axis;
}
@Override
public Component name() {
return Component.translatable("controlify.joystick_mapping.unmapped.axis", axis + 1);
}
@Override
public boolean requiresDeadzone() {
return true;
}
@Override
public float modifyAxis(float value) {
return value;
}
@Override
public boolean isAxisResting(float value) {
return value == 0;
}
@Override
public Component getDirectionName(int axis, JoystickAxisBind.AxisDirection direction) {
return Component.translatable("controlify.joystick_mapping.unmapped.axis_direction." + direction.name().toLowerCase());
}
}
@Override
public Component name() {
return Component.translatable("controlify.joystick_mapping.unmapped.axis", axis + 1);
}
@Override
public boolean requiresDeadzone() {
return true;
}
@Override
public float modifyAxis(float value) {
return value;
}
@Override
public boolean isAxisResting(float value) {
return value == 0;
}
@Override
public String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction) {
return direction.name().toLowerCase();
}
}
private record UnmappedButton(int button) implements Button {
@Override
public String identifier() {

View File

@ -0,0 +1,12 @@
package dev.isxander.controlify.debug;
public class DebugProperties {
// Renders debug overlay for vmouse snapping
public static final boolean DEBUG_SNAPPING = boolProp("controlify.debug.snapping", false);
// Forces all gamepads to be treated as a regular joystick
public static final boolean FORCE_JOYSTICK = boolProp("controlify.debug.force_joystick", false);
private static boolean boolProp(String name, boolean def) {
return Boolean.parseBoolean(System.getProperty(name, Boolean.toString(def)));
}
}

View File

@ -6,6 +6,7 @@ import com.mojang.datafixers.util.Pair;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.event.ControlifyEvents;
import dev.isxander.controlify.mixins.feature.virtualmouse.KeyboardHandlerAccessor;
@ -26,7 +27,6 @@ import java.util.Comparator;
import java.util.Set;
public class VirtualMouseHandler {
private static final boolean DEBUG_SNAPPING = FabricLoader.getInstance().isDevelopmentEnvironment();
private static final ResourceLocation CURSOR_TEXTURE = new ResourceLocation("controlify", "textures/gui/virtual_mouse.png");
private double targetX, targetY;
@ -201,7 +201,7 @@ public class VirtualMouseHandler {
public void renderVirtualMouse(PoseStack matrices) {
if (!virtualMouseEnabled) return;
if (DEBUG_SNAPPING) {
if (DebugProperties.DEBUG_SNAPPING) {
for (var snapPoint : snapPoints) {
GuiComponent.fill(matrices, snapPoint.position().x() - snapPoint.range(), snapPoint.position().y() - snapPoint.range(), snapPoint.position().x() + snapPoint.range(), snapPoint.position().y() + snapPoint.range(), 0x33FFFFFF);
GuiComponent.fill(matrices, snapPoint.position().x() - 1, snapPoint.position().y() - 1, snapPoint.position().x() + 1, snapPoint.position().y() + 1, snapPoint.equals(lastSnappedPoint) ? 0xFFFFFF00 : 0xFFFF0000);

View File

@ -1,25 +1,25 @@
// THIS FILE IS PARSED BY LENIENT GSON PARSER AND IS NOT JSON5 COMPLIANT!
[
{
"name": "Xbox One Controller",
"identifier": "xbox_one",
"vendor": 1118, // 0x45e
"product": [
767, // 0x2ff
746, // 0x2ea
2834, // 0xb12
733, // 0x2dd
739, // 0x2e3
742, // 0x2e6
765, // 0x2fd
721, // 0x2d1
649, // 0x289
514, // 0x202
645, // 0x285
648 // 0x288
]
},
// {
// "name": "Xbox One Controller",
// "identifier": "xbox_one",
//
// "vendor": 1118, // 0x45e
// "product": [
// 767, // 0x2ff
// 746, // 0x2ea
// 2834, // 0xb12
// 733, // 0x2dd
// 739, // 0x2e3
// 742, // 0x2e6
// 765, // 0x2fd
// 721, // 0x2d1
// 649, // 0x289
// 514, // 0x202
// 645, // 0x285
// 648 // 0x288
// ]
// },
{
"name": "Dualshock 4 Controller",
"identifier": "dualshock4",