forked from Clones/Controlify
✏️ Real-world gyro sensitivity
This commit is contained in:
@ -17,7 +17,7 @@ public class GamepadConfig extends ControllerConfig {
|
||||
public boolean flickStick = false;
|
||||
public boolean invertGyroX = false;
|
||||
public boolean invertGyroY = false;
|
||||
public GamepadState.GyroState gyroCalibration = GamepadState.GyroState.ORIGIN;
|
||||
public GamepadState.GyroState gyroCalibration = new GamepadState.GyroState();
|
||||
|
||||
public BuiltinGamepadTheme theme = BuiltinGamepadTheme.DEFAULT;
|
||||
|
||||
|
@ -20,7 +20,7 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
||||
private GamepadState prevState = GamepadState.EMPTY;
|
||||
|
||||
private final RumbleManager rumbleManager;
|
||||
private GamepadState.GyroState absoluteGyro = GamepadState.GyroState.ORIGIN;
|
||||
private GamepadState.GyroState absoluteGyro = new GamepadState.GyroState();
|
||||
|
||||
public final GamepadDrivers drivers;
|
||||
private final Set<Driver> uniqueDrivers;
|
||||
@ -86,8 +86,8 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
||||
}
|
||||
|
||||
// TODO: Add some sort of gyro filtering
|
||||
GamepadState.GyroState gyroState = drivers.gyroDriver().getGyroState().subtracted(config().gyroCalibration);
|
||||
this.absoluteGyro = this.absoluteGyro.added(gyroState);
|
||||
GamepadState.GyroState gyroState = new GamepadState.GyroState(drivers.gyroDriver().getGyroState()).sub(config().gyroCalibration);
|
||||
this.absoluteGyro.add(gyroState);
|
||||
|
||||
state = new GamepadState(deadzoneAxesState, basicState.axes(), basicState.buttons(), gyroState, absoluteGyro);
|
||||
}
|
||||
|
@ -3,12 +3,14 @@ package dev.isxander.controlify.controller.gamepad;
|
||||
import dev.isxander.controlify.controller.ControllerState;
|
||||
import dev.isxander.controlify.utils.ControllerUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3fc;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class GamepadState implements ControllerState {
|
||||
public static final GamepadState EMPTY = new GamepadState(AxesState.EMPTY, AxesState.EMPTY, ButtonState.EMPTY, GyroState.ORIGIN, GyroState.ORIGIN);
|
||||
public static final GamepadState EMPTY = new GamepadState(AxesState.EMPTY, AxesState.EMPTY, ButtonState.EMPTY, new GyroState(), new GyroState());
|
||||
private final AxesState gamepadAxes;
|
||||
private final AxesState rawGamepadAxes;
|
||||
private final ButtonState gamepadButtons;
|
||||
@ -24,7 +26,7 @@ public final class GamepadState implements ControllerState {
|
||||
AxesState gamepadAxes,
|
||||
AxesState rawGamepadAxes,
|
||||
ButtonState gamepadButtons,
|
||||
@Nullable GamepadState.GyroState gyroDelta,
|
||||
@Nullable GyroState gyroDelta,
|
||||
GyroState absoluteGyroPos
|
||||
) {
|
||||
this.gamepadAxes = gamepadAxes;
|
||||
@ -102,7 +104,7 @@ public final class GamepadState implements ControllerState {
|
||||
}
|
||||
|
||||
public GyroState gyroDelta() {
|
||||
if (gyroDelta == null) return GyroState.ORIGIN;
|
||||
if (gyroDelta == null) return new GyroState();
|
||||
return gyroDelta;
|
||||
}
|
||||
|
||||
@ -202,31 +204,75 @@ public final class GamepadState implements ControllerState {
|
||||
);
|
||||
}
|
||||
|
||||
public record GyroState(float pitch, float yaw, float roll) {
|
||||
public static GyroState ORIGIN = new GyroState(0, 0, 0);
|
||||
public interface GyroStateC extends Vector3fc {
|
||||
float pitch();
|
||||
|
||||
public GyroState added(GyroState other) {
|
||||
return new GyroState(pitch + other.pitch, yaw + other.yaw, roll + other.roll);
|
||||
float yaw();
|
||||
|
||||
float roll();
|
||||
}
|
||||
|
||||
public GyroState subtracted(GyroState other) {
|
||||
return new GyroState(pitch - other.pitch, yaw - other.yaw, roll - other.roll);
|
||||
public static class GyroState extends Vector3f implements GyroStateC {
|
||||
public GyroState(float pitch, float yaw, float roll) {
|
||||
super(pitch, yaw, roll);
|
||||
}
|
||||
|
||||
public GyroState multiplied(float scalar) {
|
||||
return new GyroState(pitch * scalar, yaw * scalar, roll * scalar);
|
||||
public GyroState(GyroStateC vec) {
|
||||
super(vec);
|
||||
}
|
||||
|
||||
public GyroState divided(float scalar) {
|
||||
return new GyroState(pitch / scalar, yaw / scalar, roll / scalar);
|
||||
public GyroState() {
|
||||
}
|
||||
|
||||
public GyroState deadzone(float deadzone) {
|
||||
return new GyroState(
|
||||
Math.max(pitch - deadzone, 0) + Math.min(pitch + deadzone, 0),
|
||||
Math.max(yaw - deadzone, 0) + Math.min(yaw + deadzone, 0),
|
||||
Math.max(roll - deadzone, 0) + Math.min(roll + deadzone, 0)
|
||||
);
|
||||
@Override
|
||||
public float pitch() {
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float yaw() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float roll() {
|
||||
return z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GyroState mul(Vector3fc v) {
|
||||
super.mul(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GyroState mul(float scalar) {
|
||||
super.mul(scalar);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GyroState div(Vector3fc v) {
|
||||
super.div(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GyroState div(float scalar) {
|
||||
super.div(scalar);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GyroState sub(Vector3fc v) {
|
||||
super.sub(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GyroState sub(float x, float y, float z) {
|
||||
super.sub(x, y, z);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,20 +3,22 @@ package dev.isxander.controlify.driver;
|
||||
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||
|
||||
public interface GyroDriver extends Driver {
|
||||
GamepadState.GyroState getGyroState();
|
||||
GamepadState.GyroStateC getGyroState();
|
||||
|
||||
boolean isGyroSupported();
|
||||
|
||||
String getGyroDetails();
|
||||
|
||||
GyroDriver UNSUPPORTED = new GyroDriver() {
|
||||
private final GamepadState.GyroStateC zero = new GamepadState.GyroState();
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GamepadState.GyroState getGyroState() {
|
||||
return GamepadState.GyroState.ORIGIN;
|
||||
public GamepadState.GyroStateC getGyroState() {
|
||||
return zero;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,7 +49,7 @@ public class SDL2GamepadDriver implements GyroDriver, RumbleDriver, BatteryDrive
|
||||
}
|
||||
|
||||
@Override
|
||||
public GamepadState.GyroState getGyroState() {
|
||||
public GamepadState.GyroStateC getGyroState() {
|
||||
return gyroDelta;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ public class SteamDeckDriver implements GyroDriver, BasicGamepadInputDriver {
|
||||
private final HidDevice hidDevice;
|
||||
private int interval = 0;
|
||||
|
||||
private GamepadState.GyroState gyroDelta = GamepadState.GyroState.ORIGIN;
|
||||
private GamepadState.GyroState gyroDelta = new GamepadState.GyroState();
|
||||
private BasicGamepadState basicGamepadState = new BasicGamepadState(GamepadState.AxesState.EMPTY, GamepadState.ButtonState.EMPTY);
|
||||
|
||||
public SteamDeckDriver(HidDevice hidDevice) {
|
||||
@ -114,7 +114,7 @@ public class SteamDeckDriver implements GyroDriver, BasicGamepadInputDriver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GamepadState.GyroState getGyroState() {
|
||||
public GamepadState.GyroStateC getGyroState() {
|
||||
return gyroDelta;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ public class ControllerCalibrationScreen extends Screen {
|
||||
protected int calibrationTicks = 0;
|
||||
|
||||
private final Map<Integer, double[]> deadzoneCalibration = new HashMap<>();
|
||||
private GamepadState.GyroState accumulatedGyroVelocity = GamepadState.GyroState.ORIGIN;
|
||||
private GamepadState.GyroState accumulatedGyroVelocity = new GamepadState.GyroState();
|
||||
|
||||
public ControllerCalibrationScreen(Controller<?, ?> controller, Screen parent) {
|
||||
this(controller, () -> parent);
|
||||
@ -121,7 +121,7 @@ public class ControllerCalibrationScreen extends Screen {
|
||||
if (stateChanged()) {
|
||||
calibrationTicks = 0;
|
||||
deadzoneCalibration.clear();
|
||||
accumulatedGyroVelocity = GamepadState.GyroState.ORIGIN;
|
||||
accumulatedGyroVelocity = new GamepadState.GyroState();
|
||||
}
|
||||
|
||||
if (calibrationTicks < CALIBRATION_TIME) {
|
||||
@ -154,7 +154,7 @@ public class ControllerCalibrationScreen extends Screen {
|
||||
|
||||
private void processGyroData() {
|
||||
if (controller instanceof GamepadController gamepad && gamepad.hasGyro()) {
|
||||
accumulatedGyroVelocity = accumulatedGyroVelocity.added(gamepad.drivers.gyroDriver().getGyroState());
|
||||
accumulatedGyroVelocity.add(gamepad.drivers.gyroDriver().getGyroState());
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ public class ControllerCalibrationScreen extends Screen {
|
||||
|
||||
private void generateGyroCalibration() {
|
||||
if (controller instanceof GamepadController gamepad && gamepad.hasGyro()) {
|
||||
gamepad.config().gyroCalibration = accumulatedGyroVelocity.divided(CALIBRATION_TIME);
|
||||
gamepad.config().gyroCalibration = accumulatedGyroVelocity.div(CALIBRATION_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,8 +424,8 @@ public class ControllerConfigScreenFactory {
|
||||
.build())
|
||||
.binding(gpCfgDef.gyroLookSensitivity, () -> gpCfg.gyroLookSensitivity, v -> gpCfg.gyroLookSensitivity = v)
|
||||
.controller(opt -> FloatSliderControllerBuilder.create(opt)
|
||||
.range(0f, 1f)
|
||||
.step(0.05f)
|
||||
.range(0f, 3f)
|
||||
.step(0.1f)
|
||||
.valueFormatter(percentOrOffFormatter))
|
||||
.listener((opt, sensitivity) -> gyroOptions.forEach(o -> {
|
||||
o.setAvailable(sensitivity > 0);
|
||||
|
@ -17,8 +17,10 @@ import net.minecraft.client.player.KeyboardInput;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
@ -27,12 +29,12 @@ public class InGameInputHandler {
|
||||
private final Controller<?, ?> controller;
|
||||
private final Minecraft minecraft;
|
||||
|
||||
private double lookInputX, lookInputY;
|
||||
private boolean shouldShowPlayerList;
|
||||
|
||||
private GamepadState.GyroState gyroInput = GamepadState.GyroState.ORIGIN;
|
||||
private double lookInputX, lookInputY; // in degrees per tick
|
||||
private final GamepadState.GyroState gyroInput = new GamepadState.GyroState();
|
||||
private boolean wasAiming;
|
||||
|
||||
private boolean shouldShowPlayerList;
|
||||
|
||||
private final NavigationHelper dropRepeatHelper;
|
||||
|
||||
public InGameInputHandler(Controller<?, ?> controller) {
|
||||
@ -106,15 +108,6 @@ public class InGameInputHandler {
|
||||
|
||||
minecraft.levelRenderer.needsUpdate();
|
||||
}
|
||||
|
||||
if (controller.bindings().INVENTORY.justPressed()) {
|
||||
if (minecraft.gameMode.isServerControlledInventory()) {
|
||||
minecraft.player.sendOpenInventory();
|
||||
} else {
|
||||
minecraft.getTutorial().onOpenInventory();
|
||||
minecraft.setScreen(new InventoryScreen(minecraft.player));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (controller.bindings().TOGGLE_HUD_VISIBILITY.justPressed()) {
|
||||
minecraft.options.hideGui = !minecraft.options.hideGui;
|
||||
@ -157,10 +150,12 @@ public class InGameInputHandler {
|
||||
// normal look input
|
||||
impulseY = controller.bindings().LOOK_DOWN.state() - controller.bindings().LOOK_UP.state();
|
||||
impulseX = controller.bindings().LOOK_RIGHT.state() - controller.bindings().LOOK_LEFT.state();
|
||||
impulseX *= Math.abs(impulseX);
|
||||
impulseY *= Math.abs(impulseY);
|
||||
impulseX *= Math.abs(impulseX) * 10f; // 10 degrees per second
|
||||
impulseY *= Math.abs(impulseY) * 10f;
|
||||
impulseX *= controller.config().horizontalLookSensitivity;
|
||||
impulseY *= controller.config().verticalLookSensitivity;
|
||||
|
||||
if (controller.config().reduceAimingSensitivity && player != null && player.isUsingItem()) {
|
||||
if (controller.config().reduceAimingSensitivity && player.isUsingItem()) {
|
||||
float aimMultiplier = switch (player.getUseItem().getUseAnimation()) {
|
||||
case BOW, CROSSBOW, SPEAR -> 0.6f;
|
||||
case SPYGLASS -> 0.2f;
|
||||
@ -177,23 +172,29 @@ public class InGameInputHandler {
|
||||
|
||||
if (gamepad.config().gyroRequiresButton) {
|
||||
if (gamepad.bindings().GAMEPAD_GYRO_BUTTON.justPressed() || (isAiming && !wasAiming))
|
||||
gyroInput = GamepadState.GyroState.ORIGIN;
|
||||
gyroInput.set(0);
|
||||
|
||||
if (gamepad.bindings().GAMEPAD_GYRO_BUTTON.held() || isAiming) {
|
||||
if (gamepad.config().relativeGyroMode)
|
||||
gyroInput = gyroInput.added(gamepad.state().gyroDelta().multiplied(0.1f));
|
||||
gyroInput.add(new Vector3f(gamepad.state().gyroDelta()).mul(0.1f));
|
||||
else
|
||||
gyroInput = gamepad.state().gyroDelta();
|
||||
gyroInput.set(gamepad.state().gyroDelta());
|
||||
useGyro = true;
|
||||
}
|
||||
} else {
|
||||
gyroInput = gamepad.state().gyroDelta();
|
||||
gyroInput.set(gamepad.state().gyroDelta());
|
||||
useGyro = true;
|
||||
}
|
||||
|
||||
if (useGyro) {
|
||||
impulseY += -gyroInput.pitch() * gamepad.config().gyroLookSensitivity * (gamepad.config().invertGyroY ? -1 : 1);
|
||||
impulseX += (-gyroInput.roll() + -gyroInput.yaw()) * gamepad.config().gyroLookSensitivity * (gamepad.config().invertGyroX ? -1 : 1);
|
||||
// convert radians per second into degrees per tick
|
||||
GamepadState.GyroState thisInput = new GamepadState.GyroState(gyroInput)
|
||||
.mul(Mth.RAD_TO_DEG)
|
||||
.div(20)
|
||||
.mul(gamepad.config().gyroLookSensitivity);
|
||||
|
||||
impulseY += -thisInput.pitch() * (gamepad.config().invertGyroY ? -1 : 1);
|
||||
impulseX += (-thisInput.roll() + -thisInput.yaw()) * (gamepad.config().invertGyroX ? -1 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,15 +202,15 @@ public class InGameInputHandler {
|
||||
impulseX = lookInputModifier.modifyX(impulseX, controller);
|
||||
impulseY = lookInputModifier.modifyY(impulseY, controller);
|
||||
|
||||
lookInputX = impulseX * controller.config().horizontalLookSensitivity * 65f;
|
||||
lookInputY = impulseY * controller.config().verticalLookSensitivity * 65f;
|
||||
lookInputX = impulseX;
|
||||
lookInputY = impulseY;
|
||||
|
||||
wasAiming = isAiming;
|
||||
}
|
||||
|
||||
public void processPlayerLook(float deltaTime) {
|
||||
if (minecraft.player != null) {
|
||||
minecraft.player.turn(lookInputX * deltaTime, lookInputY * deltaTime);
|
||||
minecraft.player.turn(lookInputX / 0.15f * deltaTime, lookInputY / 0.15f * deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@
|
||||
"controlify.gui.group.gyro.tooltip": "Adjust how Controlify treats your controller's built in gyroscope.\nA gyroscope determines how the controller is rotated.",
|
||||
"controlify.gui.group.gyro.no_gyro.tooltip": "This controller does not support Gyro. You must have a DualSense™ controller or other compatible controller to use this feature.",
|
||||
"controlify.gui.gyro_look_sensitivity": "Look Sensitivity",
|
||||
"controlify.gui.gyro_look_sensitivity.tooltip": "How much the camera moves based on gyroscope rotation.\nThe pitch (rotating your controller forward/backward) is used for looking up and down, whilst both the roll (rotating your controller left/right) and yaw (rotating your controller clockwise/anticlockwise) are used for looking left and right.",
|
||||
"controlify.gui.gyro_look_sensitivity.tooltip": "The percentage of the real-world rotation of your controller to use as look input. For example, rotating your controller 90 degrees, will move the camera 90 degrees, at 100%.\nThe pitch of your controller is used as up/down and the roll and yaw is used for left/right.\nThis sensitivity is completely independent of the regular horizontal and vertical sensitivity settings.",
|
||||
"controlify.gui.gyro_behaviour": "Gyro Behaviour",
|
||||
"controlify.gui.gyro_behaviour.tooltip": "How the gyroscope input should be interpreted as look input.",
|
||||
"controlify.gui.gyro_behaviour.absolute": "Absolute",
|
||||
|
Reference in New Issue
Block a user