1
0
forked from Clones/Controlify

✏️ Make rumble effects merge together rather than pausing one another when playing in unison (plays the stronger magnitude for both motors)

This commit is contained in:
isXander
2023-08-10 23:59:43 +01:00
parent 421b8abfc2
commit 58fe5c7043
9 changed files with 71 additions and 49 deletions

View File

@ -8,6 +8,8 @@ import dev.isxander.controlify.ControllerManager;
import dev.isxander.controlify.bindings.ControllerBindings; import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.hid.ControllerHIDService; import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.rumble.RumbleCapable; import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.rumble.RumbleSource;
import dev.isxander.controlify.rumble.RumbleState;
import dev.isxander.controlify.utils.Log; import dev.isxander.controlify.utils.Log;
import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.SerializationUtils;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@ -128,6 +130,14 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
return Optional.of(hidInfo); return Optional.of(hidInfo);
} }
@Override
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
float strengthMod = config().getRumbleStrength(source);
if (source != RumbleSource.MASTER)
strengthMod *= config().getRumbleStrength(RumbleSource.MASTER);
return state.mul(strengthMod);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View File

@ -7,6 +7,7 @@ import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.rumble.RumbleCapable; import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.rumble.RumbleManager; import dev.isxander.controlify.rumble.RumbleManager;
import dev.isxander.controlify.rumble.RumbleSource; import dev.isxander.controlify.rumble.RumbleSource;
import dev.isxander.controlify.rumble.RumbleState;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import java.util.Optional; import java.util.Optional;
@ -54,7 +55,7 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
private final ControllerBindings<ControllerState> bindings = new ControllerBindings<>(this); private final ControllerBindings<ControllerState> bindings = new ControllerBindings<>(this);
private final RumbleManager rumbleManager = new RumbleManager(new RumbleCapable() { private final RumbleManager rumbleManager = new RumbleManager(new RumbleCapable() {
@Override @Override
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) { public boolean setRumble(float strongMagnitude, float weakMagnitude) {
return false; return false;
} }
@ -62,6 +63,11 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
public boolean supportsRumble() { public boolean supportsRumble() {
return false; return false;
} }
@Override
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
return state;
}
}); });
private final ControllerConfig config = new ControllerConfig() { private final ControllerConfig config = new ControllerConfig() {
@Override @Override

View File

@ -8,7 +8,6 @@ import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.debug.DebugProperties; import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.driver.*; import dev.isxander.controlify.driver.*;
import dev.isxander.controlify.rumble.RumbleManager; import dev.isxander.controlify.rumble.RumbleManager;
import dev.isxander.controlify.rumble.RumbleSource;
import dev.isxander.controlify.utils.ControllerUtils; import dev.isxander.controlify.utils.ControllerUtils;
import dev.isxander.controlify.utils.Log; import dev.isxander.controlify.utils.Log;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -116,16 +115,9 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
} }
@Override @Override
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) { public boolean setRumble(float strongMagnitude, float weakMagnitude) {
if (!supportsRumble() || !config().allowVibrations) return false; if (!supportsRumble() || !config().allowVibrations) return false;
var strengthMod = config().getRumbleStrength(source);
if (source != RumbleSource.MASTER)
strengthMod *= config().getRumbleStrength(RumbleSource.MASTER);
strongMagnitude *= strengthMod;
weakMagnitude *= strengthMod;
return drivers.rumbleDriver().rumble(Math.min(strongMagnitude, 1), Math.min(weakMagnitude, 1)); return drivers.rumbleDriver().rumble(Math.min(strongMagnitude, 1), Math.min(weakMagnitude, 1));
} }

View File

@ -11,6 +11,7 @@ import dev.isxander.controlify.controller.joystick.mapping.RPJoystickMapping;
import dev.isxander.controlify.rumble.RumbleCapable; import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.rumble.RumbleManager; import dev.isxander.controlify.rumble.RumbleManager;
import dev.isxander.controlify.rumble.RumbleSource; import dev.isxander.controlify.rumble.RumbleSource;
import dev.isxander.controlify.rumble.RumbleState;
import dev.isxander.controlify.utils.Log; import dev.isxander.controlify.utils.Log;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@ -143,7 +144,7 @@ public class CompoundJoystickController implements JoystickController<JoystickCo
} }
@Override @Override
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) { public boolean setRumble(float strongMagnitude, float weakMagnitude) {
return false; return false;
} }
@ -152,6 +153,11 @@ public class CompoundJoystickController implements JoystickController<JoystickCo
return false; return false;
} }
@Override
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
return state;
}
@Override @Override
public RumbleManager rumbleManager() { public RumbleManager rumbleManager() {
return this.rumbleManager; return this.rumbleManager;

View File

@ -91,16 +91,9 @@ public class SingleJoystickController extends AbstractController<JoystickState,
} }
@Override @Override
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) { public boolean setRumble(float strongMagnitude, float weakMagnitude) {
if (!supportsRumble()) return false; if (!supportsRumble()) return false;
var strengthMod = config().getRumbleStrength(source);
if (source != RumbleSource.MASTER)
strengthMod *= config().getRumbleStrength(RumbleSource.MASTER);
strongMagnitude *= strengthMod;
weakMagnitude *= strengthMod;
// the duration doesn't matter because we are not updating the joystick state, // the duration doesn't matter because we are not updating the joystick state,
// so there is never any SDL check to stop the rumble after the desired time. // so there is never any SDL check to stop the rumble after the desired time.
if (!SDL.SDL_JoystickRumbleTriggers(ptrJoystick, (int)(strongMagnitude * 65535.0F), (int)(weakMagnitude * 65535.0F), 1)) { if (!SDL.SDL_JoystickRumbleTriggers(ptrJoystick, (int)(strongMagnitude * 65535.0F), (int)(weakMagnitude * 65535.0F), 1)) {

View File

@ -1,7 +1,9 @@
package dev.isxander.controlify.rumble; package dev.isxander.controlify.rumble;
public interface RumbleCapable { public interface RumbleCapable {
boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source); boolean setRumble(float strongMagnitude, float weakMagnitude);
boolean supportsRumble(); boolean supportsRumble();
RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source);
} }

View File

@ -30,38 +30,41 @@ public class RumbleManager {
} }
public void tick() { public void tick() {
RumbleEffectInstance effect;
do {
effect = effectQueue.peek();
// if we have no effects, break out of loop and get the null check
if (effect == null)
break;
// if the effect is finished, remove and set null, so we loop again
if (effect.effect().isFinished()) {
effectQueue.remove(effect);
effect = null;
}
} while (effect == null);
if (effect == null) {
controller.setRumble(0f, 0f, RumbleSource.MASTER);
return;
}
effectQueue.removeIf(e -> e.effect().isFinished()); effectQueue.removeIf(e -> e.effect().isFinished());
effectQueue.forEach(e -> e.effect().tick()); effectQueue.forEach(e -> e.effect().tick());
if (silent) { if (effectQueue.isEmpty()) {
if (!wasSilent) { clearRumble();
controller.setRumble(0f, 0f, RumbleSource.MASTER); return;
wasSilent = true;
}
} else {
RumbleState state = effect.effect().currentState();
controller.setRumble(state.strong(), state.weak(), effect.source());
} }
float strong = 0f, weak = 0f;
for (RumbleEffectInstance effect : effectQueue) {
RumbleState effectState = controller.applyRumbleSourceStrength(effect.effect().currentState(), effect.source());
strong = Math.max(strong, effectState.strong());
weak = Math.max(weak, effectState.weak());
}
RumbleState state = new RumbleState(strong, weak);
if (state.isZero()) {
clearRumble();
return;
}
if (silent) {
clearRumble();
} else {
controller.setRumble(state.strong(), state.weak());
wasSilent = false;
}
}
private void clearRumble() {
if (wasSilent)
return;
controller.setRumble(0f, 0f);
wasSilent = true;
} }
public void clearEffects() { public void clearEffects() {

View File

@ -3,6 +3,10 @@ package dev.isxander.controlify.rumble;
public record RumbleState(float strong, float weak) { public record RumbleState(float strong, float weak) {
public static final RumbleState NONE = new RumbleState(0.0F, 0.0F); public static final RumbleState NONE = new RumbleState(0.0F, 0.0F);
public boolean isZero() {
return strong == 0.0F && weak == 0.0F;
}
public RumbleState mul(float multiplier) { public RumbleState mul(float multiplier) {
return new RumbleState(strong * multiplier, weak * multiplier); return new RumbleState(strong * multiplier, weak * multiplier);
} }

View File

@ -14,6 +14,7 @@ import dev.isxander.controlify.controller.joystick.mapping.UnmappedJoystickMappi
import dev.isxander.controlify.rumble.RumbleCapable; import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.rumble.RumbleManager; import dev.isxander.controlify.rumble.RumbleManager;
import dev.isxander.controlify.rumble.RumbleSource; import dev.isxander.controlify.rumble.RumbleSource;
import dev.isxander.controlify.rumble.RumbleState;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -41,7 +42,7 @@ public class FakeController implements JoystickController<JoystickConfig> {
this.config = new JoystickConfig(this); this.config = new JoystickConfig(this);
this.rumbleManager = new RumbleManager(new RumbleCapable() { this.rumbleManager = new RumbleManager(new RumbleCapable() {
@Override @Override
public boolean setRumble(float strongMagnitude, float weakMagnitude, RumbleSource source) { public boolean setRumble(float strongMagnitude, float weakMagnitude) {
return false; return false;
} }
@ -49,6 +50,11 @@ public class FakeController implements JoystickController<JoystickConfig> {
public boolean supportsRumble() { public boolean supportsRumble() {
return false; return false;
} }
@Override
public RumbleState applyRumbleSourceStrength(RumbleState state, RumbleSource source) {
return state;
}
}); });
this.config.deadzonesCalibrated = true; this.config.deadzonesCalibrated = true;
} }