1
0
forked from Clones/Controlify

(unused) abstract joystick renderer system + debug logging + tm joystick mapping

This commit is contained in:
isXander
2023-04-14 15:29:52 +01:00
parent 6092c896fe
commit bf874fef4f
25 changed files with 598 additions and 267 deletions

View File

@ -8,6 +8,7 @@ import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import dev.isxander.controlify.controller.joystick.CompoundJoystickController;
import dev.isxander.controlify.controller.sdl2.SDL2NativesManager;
import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.gui.screen.ControllerDeadzoneCalibrationScreen;
import dev.isxander.controlify.gui.screen.VibrationOnboardingScreen;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
@ -17,6 +18,7 @@ import dev.isxander.controlify.api.event.ControlifyEvents;
import dev.isxander.controlify.ingame.guide.InGameButtonGuide;
import dev.isxander.controlify.ingame.InGameInputHandler;
import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor;
import dev.isxander.controlify.utils.DebugLog;
import dev.isxander.controlify.utils.ToastUtils;
import dev.isxander.controlify.virtualmouse.VirtualMouseHandler;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
@ -74,9 +76,7 @@ public class Controlify implements ControlifyApi {
}
private void initializeControllers() {
LOGGER.info("Discovering and initializing controllers...");
config().load();
DebugLog.log("Discovering and initializing controllers...");
if (config().globalSettings().loadVibrationNatives)
SDL2NativesManager.initialise();
@ -140,6 +140,8 @@ public class Controlify implements ControlifyApi {
}
public void preInitialiseControlify() {
DebugProperties.printProperties();
LOGGER.info("Pre-initializing Controlify...");
this.inGameInputHandler = new InGameInputHandler(Controller.DUMMY); // initialize with dummy controller before connection in case of no controller
@ -205,7 +207,7 @@ public class Controlify implements ControlifyApi {
if (state.hasAnyInput())
this.setInputMode(InputMode.CONTROLLER);
if (consecutiveInputSwitches > 500) {
if (consecutiveInputSwitches > 100) {
LOGGER.warn("Controlify detected current controller to be constantly giving input and has been disabled.");
ToastUtils.sendToast(
Component.translatable("controlify.toast.faulty_input.title"),
@ -289,13 +291,13 @@ public class Controlify implements ControlifyApi {
try {
if (info.isLoaded() && !info.canBeUsed()) {
LOGGER.warn("Unloading compound joystick " + info.friendlyName() + " due to missing controllers.");
Controller.CONTROLLERS.remove(info.type().identifier());
Controller.CONTROLLERS.remove(info.type().mappingId());
}
if (!info.isLoaded() && info.canBeUsed()) {
LOGGER.info("Loading compound joystick " + info.type().identifier() + ".");
LOGGER.info("Loading compound joystick " + info.type().mappingId() + ".");
CompoundJoystickController controller = info.attemptCreate().orElseThrow();
Controller.CONTROLLERS.put(info.type().identifier(), controller);
Controller.CONTROLLERS.put(info.type().mappingId(), controller);
config().loadOrCreateControllerData(controller);
}
} catch (Exception e) {
@ -339,14 +341,14 @@ public class Controlify implements ControlifyApi {
switchableController = null;
}
LOGGER.info("Updated current controller to " + controller.name() + "(" + controller.uid() + ")");
DebugLog.log("Updated current controller to {}({})", controller.name(), controller.uid());
if (!config().currentControllerUid().equals(controller.uid())) {
config().save();
}
this.inGameInputHandler = new InGameInputHandler(controller);
if (Minecraft.getInstance().player != null) {
if (minecraft.player != null) {
this.inGameButtonGuide = new InGameButtonGuide(controller, Minecraft.getInstance().player);
}

View File

@ -65,7 +65,7 @@ public class GamepadBind implements IBind<GamepadState> {
private ResourceLocation getTexture(BuiltinGamepadTheme theme) {
String themeId = theme.id();
if (theme == BuiltinGamepadTheme.DEFAULT)
themeId = gamepad.type().identifier();
themeId = gamepad.type().themeId();
return new ResourceLocation("controlify", "textures/gui/gamepad/" + themeId + "/" + identifier + ".png");
}

View File

@ -41,7 +41,7 @@ public class JoystickAxisBind implements IBind<JoystickState> {
public void draw(PoseStack matrices, int x, int centerY) {
JoystickMapping mapping = joystick.mapping();
String type = joystick.type().identifier();
String type = joystick.type().themeId();
String axis = mapping.axes()[axisIndex].identifier();
String direction = mapping.axes()[axisIndex].getDirectionIdentifier(axisIndex, this.direction);
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/axis_" + axis + "_" + direction + ".png");

View File

@ -30,7 +30,7 @@ public class JoystickButtonBind implements IBind<JoystickState> {
@Override
public void draw(PoseStack matrices, int x, int centerY) {
String type = joystick.type().identifier();
String type = joystick.type().themeId();
String button = joystick.mapping().buttons()[buttonIndex].identifier();
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/button_" + button + ".png");

View File

@ -32,7 +32,7 @@ public class JoystickHatBind implements IBind<JoystickState> {
@Override
public void draw(PoseStack matrices, int x, int centerY) {
String type = joystick.type().identifier();
String type = joystick.type().themeId();
String hat = joystick.mapping().hats()[hatIndex].identifier();
String direction = "centered";
if (hatState.isUp())

View File

@ -4,6 +4,7 @@ import com.google.gson.*;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.joystick.CompoundJoystickInfo;
import dev.isxander.controlify.utils.DebugLog;
import net.fabricmc.loader.api.FabricLoader;
import java.io.IOException;
@ -66,7 +67,7 @@ public class ControlifyConfig {
}
if (dirty) {
Controlify.LOGGER.info("Config was dirty after load, saving...");
DebugLog.log("Config was dirty after load, saving...");
save();
}
}
@ -121,7 +122,7 @@ public class ControlifyConfig {
.asList()
.stream()
.map(element -> GSON.fromJson(element, CompoundJoystickInfo.class))
.collect(Collectors.toMap(info -> info.type().identifier(), Function.identity()));
.collect(Collectors.toMap(info -> info.type().mappingId(), Function.identity()));
if (object.has("current_controller")) {
currentControllerUid = object.get("current_controller").getAsString();
@ -134,10 +135,10 @@ public class ControlifyConfig {
public void loadOrCreateControllerData(Controller<?, ?> controller) {
var uid = controller.uid();
if (controllerData.has(uid)) {
Controlify.LOGGER.info("Loading controller data for " + uid);
DebugLog.log("Loading controller data for " + uid);
applyControllerConfig(controller, controllerData.getAsJsonObject(uid));
} else {
Controlify.LOGGER.info("New controller found, creating controller data for " + uid);
DebugLog.log("New controller found, setting config dirty ({})", uid);
setDirty();
}
}
@ -159,7 +160,7 @@ public class ControlifyConfig {
public void saveIfDirty() {
if (dirty) {
Controlify.LOGGER.info("Config is dirty. Saving...");
DebugLog.log("Config is dirty. Saving...");
save();
}
}

View File

@ -237,7 +237,7 @@ public class YACLHelper {
}
category.group(vibrationGroup.build());
if (controller instanceof GamepadController gamepad && (gamepad.hasGyro() || true)) {
if (controller instanceof GamepadController gamepad && gamepad.hasGyro()) {
var gpCfg = gamepad.config();
var gpCfgDef = gamepad.defaultConfig();

View File

@ -11,6 +11,7 @@ import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.rumble.RumbleManager;
import dev.isxander.controlify.rumble.RumbleSource;
import dev.isxander.controlify.utils.DebugLog;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
@ -59,7 +60,7 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
}
if (hidInfo.type().dontLoad()) {
Controlify.LOGGER.warn("Preventing load of controller #" + joystickId + " because its type prevents loading.");
DebugLog.log("Preventing load of controller #" + joystickId + " because its type prevents loading.");
return Optional.empty();
}

View File

@ -11,8 +11,8 @@ import org.quiltmc.json5.JsonReader;
import java.io.IOException;
import java.util.*;
public record ControllerType(String friendlyName, String identifier, boolean forceJoystick, boolean dontLoad) {
public static final ControllerType UNKNOWN = new ControllerType("Unknown", "unknown", false, false);
public record ControllerType(String friendlyName, String mappingId, String themeId, boolean forceJoystick, boolean dontLoad) {
public static final ControllerType UNKNOWN = new ControllerType("Unknown", "unknown", "unknown", false, false);
private static Map<HIDIdentifier, ControllerType> typeMap = null;
private static final ResourceLocation hidDbLocation = new ResourceLocation("controlify", "controllers/controller_identification.json5");
@ -46,7 +46,9 @@ public record ControllerType(String friendlyName, String identifier, boolean for
reader.beginArray();
while (reader.hasNext()) {
String friendlyName = null;
String identifier = null;
String legacyIdentifier = null;
String themeId = null;
String mappingId = null;
boolean forceJoystick = false;
boolean dontLoad = false;
Set<HIDIdentifier> hids = new HashSet<>();
@ -57,7 +59,9 @@ public record ControllerType(String friendlyName, String identifier, boolean for
switch (name) {
case "name" -> friendlyName = reader.nextString();
case "identifier" -> identifier = reader.nextString();
case "identifier" -> legacyIdentifier = reader.nextString();
case "theme" -> themeId = reader.nextString();
case "mapping" -> mappingId = reader.nextString();
case "hids" -> {
reader.beginArray();
while (reader.hasNext()) {
@ -89,12 +93,18 @@ public record ControllerType(String friendlyName, String identifier, boolean for
}
reader.endObject();
if (friendlyName == null || identifier == null || hids.isEmpty()) {
if (legacyIdentifier != null) {
Controlify.LOGGER.warn("Legacy identifier found in HID DB. Please replace with `theme` and `mapping` (if needed).");
themeId = legacyIdentifier;
mappingId = legacyIdentifier;
}
if (friendlyName == null || themeId == null || hids.isEmpty()) {
Controlify.LOGGER.warn("Invalid entry in HID DB. Skipping...");
continue;
}
var type = new ControllerType(friendlyName, identifier, forceJoystick, dontLoad);
var type = new ControllerType(friendlyName, mappingId, themeId, forceJoystick, dontLoad);
for (var hid : hids) {
typeMap.put(hid, type);
}

View File

@ -129,7 +129,6 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
public void open() {
if (SDL2NativesManager.isLoaded()) {
this.gamepadPtr = SDL.SDL_GameControllerOpen(joystickId);
Controlify.LOGGER.info(SDL.SDL_GetError());
this.rumbleSupported = SDL.SDL_GameControllerHasRumble(gamepadPtr);
this.triggerRumbleSupported = SDL.SDL_GameControllerHasRumble(gamepadPtr);
if (this.hasGyro = SDL.SDL_GameControllerHasSensor(gamepadPtr, SDL.SDL_SENSOR_GYRO)) {

View File

@ -9,7 +9,7 @@ import java.util.Optional;
public record CompoundJoystickInfo(Collection<String> joystickUids, String friendlyName) {
public ControllerType type() {
return new ControllerType(friendlyName, createUID(joystickUids), true, false);
return new ControllerType(friendlyName, createUID(joystickUids), "generic", true, false);
}
public boolean canBeUsed() {
@ -37,7 +37,7 @@ public record CompoundJoystickInfo(Collection<String> joystickUids, String frien
.toList();
ControllerType type = type();
return Optional.of(new CompoundJoystickController(joystickIDs, type.identifier(), type));
return Optional.of(new CompoundJoystickController(joystickIDs, type.mappingId(), type));
}
public static String createUID(Collection<String> joystickUIDs) {

View File

@ -2,6 +2,7 @@ package dev.isxander.controlify.controller.joystick.mapping;
import dev.isxander.controlify.bindings.JoystickAxisBind;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.controller.joystick.render.JoystickRenderer;
import net.minecraft.network.chat.Component;
public interface JoystickMapping {
@ -23,6 +24,8 @@ public interface JoystickMapping {
float restingValue();
String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction);
JoystickRenderer renderer();
}
interface Button {
@ -31,6 +34,8 @@ public interface JoystickMapping {
Component name();
boolean isPressed(JoystickData data);
JoystickRenderer renderer();
}
interface Hat {
@ -39,6 +44,8 @@ public interface JoystickMapping {
String identifier();
Component name();
JoystickRenderer renderer(JoystickState.HatState state);
}
record JoystickData(float[] axes, boolean[] buttons, JoystickState.HatState[] hats) {

View File

@ -5,6 +5,7 @@ import dev.isxander.controlify.bindings.JoystickAxisBind;
import dev.isxander.controlify.controller.ControllerType;
import dev.isxander.controlify.controller.joystick.JoystickController;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.controller.joystick.render.JoystickRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@ -141,7 +142,7 @@ public class RPJoystickMapping implements JoystickMapping {
reader.endObject();
for (var id : ids) {
axes.add(new AxisMapping(id, identifier, inpRange, outRange, restState, deadzone, type.identifier(), axisNames.get(ids.indexOf(id))));
axes.add(new AxisMapping(id, identifier, inpRange, outRange, restState, deadzone, type.mappingId(), axisNames.get(ids.indexOf(id))));
}
}
reader.endArray();
@ -171,7 +172,7 @@ public class RPJoystickMapping implements JoystickMapping {
}
reader.endObject();
buttons.add(new ButtonMapping(id, btnName, type.identifier()));
buttons.add(new ButtonMapping(id, btnName, type.mappingId()));
}
reader.endArray();
@ -231,7 +232,7 @@ public class RPJoystickMapping implements JoystickMapping {
}
reader.endObject();
hats.add(new HatMapping(id, hatName, type.identifier(), axis));
hats.add(new HatMapping(id, hatName, type.mappingId(), axis));
}
reader.endArray();
@ -254,21 +255,21 @@ public class RPJoystickMapping implements JoystickMapping {
}
public static JoystickMapping fromType(JoystickController<?> joystick) {
var resource = Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation("controlify", "mappings/" + joystick.type().identifier() + ".json"));
var resource = Minecraft.getInstance().getResourceManager().getResource(new ResourceLocation("controlify", "mappings/" + joystick.type().mappingId() + ".json"));
if (resource.isEmpty()) {
Controlify.LOGGER.warn("No joystick mapping found for controller: '" + joystick.type().identifier() + "'");
Controlify.LOGGER.warn("No joystick mapping found for controller: '" + joystick.type().mappingId() + "'");
return new UnmappedJoystickMapping(joystick.joystickId());
}
try (var reader = JsonReader.json5(resource.get().openAsReader())) {
return new RPJoystickMapping(reader, joystick.type());
} catch (Exception e) {
Controlify.LOGGER.error("Failed to load joystick mapping for controller: '" + joystick.type().identifier() + "'", e);
Controlify.LOGGER.error("Failed to load joystick mapping for controller: '" + joystick.type().mappingId() + "'", e);
return new UnmappedJoystickMapping(joystick.joystickId());
}
}
private record AxisMapping(int id, String identifier, Vec2 inpRange, Vec2 outRange, float restingValue, boolean requiresDeadzone, String typeId, String[] axisNames) implements Axis {
private record AxisMapping(int id, String identifier, Vec2 inpRange, Vec2 outRange, float restingValue, boolean requiresDeadzone, String theme, String[] axisNames) implements Axis {
@Override
public float getAxis(JoystickData data) {
float rawAxis = data.axes()[id];
@ -286,13 +287,18 @@ public class RPJoystickMapping implements JoystickMapping {
@Override
public Component name() {
return Component.translatable("controlify.joystick_mapping." + typeId() + ".axis." + identifier());
return Component.translatable("controlify.joystick_mapping." + theme() + ".axis." + identifier());
}
@Override
public String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction) {
return this.axisNames()[direction.ordinal()];
}
@Override
public JoystickRenderer renderer() {
return null;
}
}
private record ButtonMapping(int id, String identifier, String typeId) implements Button {
@ -305,6 +311,11 @@ public class RPJoystickMapping implements JoystickMapping {
public Component name() {
return Component.translatable("controlify.joystick_mapping." + typeId() + ".button." + identifier());
}
@Override
public JoystickRenderer renderer() {
return null;
}
}
private record HatMapping(int hatId, String identifier, String typeId, @Nullable EmulatedAxis emulatedAxis) implements Hat {
@ -324,6 +335,11 @@ public class RPJoystickMapping implements JoystickMapping {
return Component.translatable("controlify.joystick_mapping." + typeId() + ".hat." + identifier());
}
@Override
public JoystickRenderer renderer(JoystickState.HatState state) {
return null;
}
private record EmulatedAxis(int axisId, Map<Float, JoystickState.HatState> states) {
}
}

View File

@ -1,13 +1,12 @@
package dev.isxander.controlify.controller.joystick.mapping;
import dev.isxander.controlify.bindings.JoystickAxisBind;
import dev.isxander.controlify.controller.joystick.JoystickController;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.controller.joystick.render.GenericRenderer;
import dev.isxander.controlify.controller.joystick.render.JoystickRenderer;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW;
import java.util.Arrays;
public class UnmappedJoystickMapping implements JoystickMapping {
public static final UnmappedJoystickMapping EMPTY = new UnmappedJoystickMapping(0, 0, 0);
@ -18,12 +17,12 @@ public class UnmappedJoystickMapping implements JoystickMapping {
private UnmappedJoystickMapping(int axisCount, int buttonCount, int hatCount) {
this.axes = new UnmappedAxis[axisCount];
for (int i = 0; i < axisCount; i++) {
this.axes[i] = new UnmappedAxis(i);
this.axes[i] = new UnmappedAxis(i, new GenericRenderer.Axis(Integer.toString(i + 1)));
}
this.buttons = new UnmappedButton[axisCount];
for (int i = 0; i < buttonCount; i++) {
this.buttons[i] = new UnmappedButton(i);
this.buttons[i] = new UnmappedButton(i, new GenericRenderer.Button(Integer.toString(i + 1)));
}
this.hats = new UnmappedHat[hatCount];
@ -55,7 +54,7 @@ public class UnmappedJoystickMapping implements JoystickMapping {
return hats;
}
private record UnmappedAxis(int axis) implements Axis {
private record UnmappedAxis(int axis, GenericRenderer.Axis renderer) implements Axis {
@Override
public float getAxis(JoystickData data) {
return data.axes()[axis];
@ -92,7 +91,7 @@ public class UnmappedJoystickMapping implements JoystickMapping {
}
}
private record UnmappedButton(int button) implements Button {
private record UnmappedButton(int button, GenericRenderer.Button renderer) implements Button {
@Override
public boolean isPressed(JoystickData data) {
return data.buttons()[button];
@ -124,5 +123,10 @@ public class UnmappedJoystickMapping implements JoystickMapping {
public Component name() {
return Component.translatable("controlify.joystick_mapping.unmapped.hat", hat + 1);
}
@Override
public JoystickRenderer renderer(JoystickState.HatState state) {
return new GenericRenderer.Hat(Integer.toString(hat + 1));
}
}
}

View File

@ -0,0 +1,144 @@
package dev.isxander.controlify.controller.joystick.render;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.bindings.JoystickAxisBind;
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.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
public abstract class GenericRenderer implements JoystickRenderer {
protected final @Nullable String annotation;
protected final Minecraft minecraft = Minecraft.getInstance();
public GenericRenderer(@Nullable String annotation) {
this.annotation = annotation;
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size) {
if (annotation != null) {
minecraft.font.draw(
poseStack,
annotation,
x + size + 2 - minecraft.font.width(annotation),
centerY + size/2f - minecraft.font.lineHeight * 0.75f,
-1
);
}
return null;
}
public static class Button extends GenericRenderer implements JoystickRenderer.Button {
private static final ResourceLocation BUTTON_TEXTURE = new ResourceLocation("controlify", "textures/gui/joystick/generic/button.png");
public Button(@Nullable String annotation) {
super(annotation);
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size) {
RenderSystem.setShaderTexture(0, BUTTON_TEXTURE);
RenderSystem.setShaderColor(1, 1, 1, 1);
poseStack.pushPose();
poseStack.translate(x, centerY, 0);
float scale = (float) size / 22f;
poseStack.scale(scale, scale, 1);
poseStack.translate(0f, -DEFAULT_SIZE / scale / 2f, 0);
GuiComponent.blit(poseStack, 0, 0, 0, 0, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE);
poseStack.popPose();
super.render(poseStack, x, centerY, size);
return new DrawSize(size, size);
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size, boolean down) {
return this.render(poseStack, x, centerY, size);
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, boolean down) {
return this.render(poseStack, x, centerY, DEFAULT_SIZE);
}
}
public static class Axis extends GenericRenderer implements JoystickRenderer.Axis {
private static final ResourceLocation AXIS_TEXTURE = new ResourceLocation("controlify", "textures/gui/joystick/generic/axis.png");
public Axis(@Nullable String annotation) {
super(annotation);
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size, JoystickAxisBind.AxisDirection direction) {
RenderSystem.setShaderTexture(0, AXIS_TEXTURE);
RenderSystem.setShaderColor(1, 1, 1, 1);
poseStack.pushPose();
poseStack.translate(x, centerY, 0);
float scale = (float) size / 22f;
poseStack.scale(scale, scale, 1);
poseStack.translate(0f, -DEFAULT_SIZE / scale / 2f, 0);
GuiComponent.blit(
poseStack,
0, 0,
direction.ordinal() * DEFAULT_SIZE, 0,
DEFAULT_SIZE, DEFAULT_SIZE,
DEFAULT_SIZE * JoystickAxisBind.AxisDirection.values().length, DEFAULT_SIZE
);
poseStack.popPose();
super.render(poseStack, x, centerY, size);
return new DrawSize(size, size);
}
}
public static class Hat extends GenericRenderer implements JoystickRenderer.Hat {
private static final ResourceLocation HAT_TEXTURE = new ResourceLocation("controlify", "textures/gui/joystick/generic/hat.png");
public Hat(@Nullable String annotation) {
super(annotation);
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size, JoystickState.HatState hatState) {
RenderSystem.setShaderTexture(0, HAT_TEXTURE);
RenderSystem.setShaderColor(1, 1, 1, 1);
poseStack.pushPose();
poseStack.translate(x, centerY, 0);
float scale = (float) size / 22f;
poseStack.scale(scale, scale, 1);
poseStack.translate(0f, -DEFAULT_SIZE / scale / 2f, 0);
GuiComponent.blit(
poseStack,
0, 0,
hatState.ordinal() * DEFAULT_SIZE, 0,
DEFAULT_SIZE, DEFAULT_SIZE,
DEFAULT_SIZE * JoystickState.HatState.values().length, DEFAULT_SIZE
);
poseStack.popPose();
super.render(poseStack, x, centerY, size);
return new DrawSize(size, size);
}
}
}

View File

@ -0,0 +1,48 @@
package dev.isxander.controlify.controller.joystick.render;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.bindings.JoystickAxisBind;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.gui.DrawSize;
public interface JoystickRenderer {
int DEFAULT_SIZE = 22;
DrawSize render(PoseStack poseStack, int x, int centerY, int size);
interface Button extends JoystickRenderer {
DrawSize render(PoseStack poseStack, int x, int centerY, int size, boolean down);
default DrawSize render(PoseStack poseStack, int x, int centerY, boolean down) {
return render(poseStack, x, centerY, DEFAULT_SIZE, down);
}
default DrawSize render(PoseStack poseStack, int x, int centerY, int size) {
return render(poseStack, x, centerY, size, false);
}
}
interface Axis extends JoystickRenderer {
DrawSize render(PoseStack poseStack, int x, int centerY, int size, JoystickAxisBind.AxisDirection direction);
default DrawSize render(PoseStack poseStack, int x, int centerY, JoystickAxisBind.AxisDirection direction) {
return render(poseStack, x, centerY, DEFAULT_SIZE, direction);
}
default DrawSize render(PoseStack poseStack, int x, int centerY, int size) {
return render(poseStack, x, centerY, size, JoystickAxisBind.AxisDirection.POSITIVE);
}
}
interface Hat extends JoystickRenderer {
DrawSize render(PoseStack poseStack, int x, int centerY, int size, JoystickState.HatState state);
default DrawSize render(PoseStack poseStack, int x, int centerY, JoystickState.HatState state) {
return render(poseStack, x, centerY, DEFAULT_SIZE, state);
}
default DrawSize render(PoseStack poseStack, int x, int centerY, int size) {
return render(poseStack, x, centerY, size, JoystickState.HatState.CENTERED);
}
}
}

View File

@ -0,0 +1,116 @@
package dev.isxander.controlify.controller.joystick.render;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.bindings.JoystickAxisBind;
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.resources.ResourceLocation;
public abstract class ThemedRenderer {
protected final Minecraft minecraft = Minecraft.getInstance();
protected final String themeId;
public ThemedRenderer(String themeId) {
this.themeId = themeId;
}
public static class Button extends ThemedRenderer implements JoystickRenderer.Button {
private final ResourceLocation texture;
public Button(String themeId, String identifier) {
super(themeId);
this.texture = new ResourceLocation("controlify", "textures/gui/joystick/" + themeId + "/" + identifier + ".png");
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size, boolean down) {
RenderSystem.setShaderTexture(0, texture);
RenderSystem.setShaderColor(1, 1, 1, 1);
poseStack.pushPose();
poseStack.translate(x, centerY, 0);
float scale = (float) size / 22f;
poseStack.scale(scale, scale, 1);
poseStack.translate(0f, -DEFAULT_SIZE / scale / 2f, 0);
GuiComponent.blit(poseStack, 0, 0, 0, 0, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE);
poseStack.popPose();
return new DrawSize(size, size);
}
}
public static class Axis extends ThemedRenderer implements JoystickRenderer.Axis {
private final ResourceLocation texture;
public Axis(String themeId, String identifier) {
super(themeId);
this.texture = new ResourceLocation("controlify", "textures/gui/joystick/" + themeId + "/" + identifier + ".png");
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size, JoystickAxisBind.AxisDirection direction) {
RenderSystem.setShaderTexture(0, texture);
RenderSystem.setShaderColor(1, 1, 1, 1);
poseStack.pushPose();
poseStack.translate(x, centerY, 0);
float scale = (float) size / 22f;
poseStack.scale(scale, scale, 1);
poseStack.translate(0f, -DEFAULT_SIZE / scale / 2f, 0);
GuiComponent.blit(
poseStack,
0, 0,
direction.ordinal() * DEFAULT_SIZE, 0,
DEFAULT_SIZE, DEFAULT_SIZE,
DEFAULT_SIZE * JoystickAxisBind.AxisDirection.values().length, DEFAULT_SIZE
);
poseStack.popPose();
return new DrawSize(size, size);
}
}
public static class Hat extends ThemedRenderer implements JoystickRenderer.Hat {
private final ResourceLocation texture;
public Hat(String themeId, String identifier) {
super(themeId);
this.texture = new ResourceLocation("controlify", "textures/gui/joystick/" + themeId + "/" + identifier + ".png");
}
@Override
public DrawSize render(PoseStack poseStack, int x, int centerY, int size, JoystickState.HatState hatState) {
RenderSystem.setShaderTexture(0, texture);
RenderSystem.setShaderColor(1, 1, 1, 1);
poseStack.pushPose();
poseStack.translate(x, centerY, 0);
float scale = (float) size / 22f;
poseStack.scale(scale, scale, 1);
poseStack.translate(0f, -DEFAULT_SIZE / scale / 2f, 0);
GuiComponent.blit(
poseStack,
0, 0,
hatState.ordinal() * DEFAULT_SIZE, 0,
DEFAULT_SIZE, DEFAULT_SIZE,
DEFAULT_SIZE * JoystickState.HatState.values().length, DEFAULT_SIZE
);
poseStack.popPose();
return new DrawSize(size, size);
}
}
}

View File

@ -1,6 +1,7 @@
package dev.isxander.controlify.controller.sdl2;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.utils.DebugLog;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.Util;
import org.libsdl.SDL;
@ -31,7 +32,7 @@ public class SDL2NativesManager {
public static void initialise() {
if (loaded) return;
Controlify.LOGGER.info("Initialising SDL2 native library");
DebugLog.log("Initialising SDL2 native library");
if (!Target.CURRENT.hasNativeLibrary()) {
Controlify.LOGGER.warn("SDL2 native library not available for OS: " + Target.CURRENT);
@ -77,7 +78,7 @@ public class SDL2NativesManager {
throw new RuntimeException("Failed to initialise SDL2: " + SDL.SDL_GetError());
}
Controlify.LOGGER.info("Initialised SDL2");
DebugLog.log("Initialised SDL2");
}
private static boolean downloadLibrary(Path path) {

View File

@ -1,19 +1,49 @@
package dev.isxander.controlify.debug;
import dev.isxander.controlify.Controlify;
import net.fabricmc.loader.api.FabricLoader;
import java.util.ArrayList;
import java.util.List;
public class DebugProperties {
private static final List<DebugProperty> properties = new ArrayList<>();
public static final boolean DEBUG_LOGGING = boolProp("controlify.debug.logging", false, true);
/* Renders debug overlay for vmouse snapping */
public static final boolean DEBUG_SNAPPING = boolProp("controlify.debug.snapping", false, false);
/* Forces all gamepads to be treated as a regular joystick */
public static final boolean FORCE_JOYSTICK = boolProp("controlify.debug.force_joystick", false, false);
/* Prints joystick input counts for making joystick mappings */
public static final boolean PRINT_JOY_INPUT_COUNT = boolProp("controlify.debug.print_joy_input_count", false, true);
public static final boolean PRINT_JOY_INPUT_COUNT = boolProp("controlify.debug.print_joy_input_count", false, false);
/* Print gyro data if supported */
public static final boolean PRINT_GYRO = boolProp("controlify.debug.print_gyro", false, false);
public static void printProperties() {
if (properties.stream().noneMatch(DebugProperty::enabled))
return;
String header = "*----------------- Controlify Debug Properties -----------------*";
Controlify.LOGGER.error(header);
int maxWidth = properties.stream().mapToInt(prop -> prop.name().length()).max().orElse(0);
for (var prop : properties) {
String line = "| %s%s = %s".formatted(prop.name(), " ".repeat(maxWidth - prop.name().length()), prop.enabled());
line += " ".repeat(header.length() - line.length() - 1) + "|";
Controlify.LOGGER.error(line);
}
Controlify.LOGGER.error("*---------------------------------------------------------------*");
}
private static boolean boolProp(String name, boolean defProd, boolean defDev) {
boolean def = FabricLoader.getInstance().isDevelopmentEnvironment() ? defDev : defProd;
return Boolean.parseBoolean(System.getProperty(name, Boolean.toString(def)));
boolean enabled = Boolean.parseBoolean(System.getProperty(name, Boolean.toString(def)));
properties.add(new DebugProperty(name, enabled));
return enabled;
}
private record DebugProperty(String name, boolean enabled) {
}
}

View File

@ -0,0 +1,12 @@
package dev.isxander.controlify.utils;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.debug.DebugProperties;
public class DebugLog {
public static void log(String message, Object... args) {
if (DebugProperties.DEBUG_LOGGING) {
Controlify.LOGGER.info(message, args);
}
}
}