ControllerEmu: Add new "input override" system

This commit is contained in:
JosJuice 2021-03-21 20:27:00 +01:00
parent cb6d476538
commit cb16d20f2d
27 changed files with 355 additions and 74 deletions

View File

@ -138,14 +138,15 @@ GCPadStatus GCPad::GetInput() const
const auto lock = GetStateLock();
GCPadStatus pad = {};
if (!(m_always_connected_setting.GetValue() || IsDefaultDeviceConnected()))
if (!(m_always_connected_setting.GetValue() || IsDefaultDeviceConnected() ||
m_input_override_function))
{
pad.isConnected = false;
return pad;
}
// buttons
m_buttons->GetState(&pad.button, button_bitmasks);
m_buttons->GetState(&pad.button, button_bitmasks, m_input_override_function);
// set analog A/B analog to full or w/e, prolly not needed
if (pad.button & PAD_BUTTON_A)
@ -154,20 +155,20 @@ GCPadStatus GCPad::GetInput() const
pad.analogB = 0xFF;
// dpad
m_dpad->GetState(&pad.button, dpad_bitmasks);
m_dpad->GetState(&pad.button, dpad_bitmasks, m_input_override_function);
// sticks
const auto main_stick_state = m_main_stick->GetState();
const auto main_stick_state = m_main_stick->GetState(m_input_override_function);
pad.stickX = MapFloat<u8>(main_stick_state.x, GCPadStatus::MAIN_STICK_CENTER_X, 1);
pad.stickY = MapFloat<u8>(main_stick_state.y, GCPadStatus::MAIN_STICK_CENTER_Y, 1);
const auto c_stick_state = m_c_stick->GetState();
const auto c_stick_state = m_c_stick->GetState(m_input_override_function);
pad.substickX = MapFloat<u8>(c_stick_state.x, GCPadStatus::C_STICK_CENTER_X, 1);
pad.substickY = MapFloat<u8>(c_stick_state.y, GCPadStatus::C_STICK_CENTER_Y, 1);
// triggers
std::array<ControlState, 2> triggers;
m_triggers->GetState(&pad.button, trigger_bitmasks, triggers.data());
m_triggers->GetState(&pad.button, trigger_bitmasks, triggers.data(), m_input_override_function);
pad.triggerLeft = MapFloat<u8>(triggers[0], 0);
pad.triggerRight = MapFloat<u8>(triggers[1], 0);

View File

@ -5,6 +5,7 @@
#include <algorithm>
#include <cmath>
#include <optional>
#include "Common/MathUtil.h"
#include "Core/Config/SYSCONFSettings.h"
@ -221,9 +222,10 @@ WiimoteCommon::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g,
u16(std::clamp(std::lround(scaled_accel.z + zero_g), 0l, MAX_VALUE))});
}
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed)
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group,
const ControllerEmu::InputOverrideFunction& override_func, float time_elapsed)
{
const auto cursor = ir_group->GetState(true);
const auto cursor = ir_group->GetState(true, override_func);
if (!cursor.IsVisible())
{

View File

@ -15,6 +15,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
namespace WiimoteEmu
{
@ -81,7 +82,8 @@ void ApproachAngleWithAccel(RotationalState* state, const Common::Vec3& target,
void EmulateShake(PositionalState* state, ControllerEmu::Shake* shake_group, float time_elapsed);
void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* tilt_group, float time_elapsed);
void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float time_elapsed);
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed);
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group,
const ControllerEmu::InputOverrideFunction& override_func, float time_elapsed);
void EmulateIMUCursor(IMUCursorState* state, ControllerEmu::IMUCursor* imu_ir_group,
ControllerEmu::IMUAccelerometer* imu_accelerometer_group,
ControllerEmu::IMUGyroscope* imu_gyroscope_group, float time_elapsed);

View File

@ -113,7 +113,8 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// left stick
{
const ControllerEmu::AnalogStick::StateData left_stick_state = m_left_stick->GetState();
const ControllerEmu::AnalogStick::StateData left_stick_state =
m_left_stick->GetState(m_input_override_function);
const u8 x = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.x * LEFT_STICK_RADIUS));
const u8 y = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.y * LEFT_STICK_RADIUS));
@ -123,7 +124,8 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// right stick
{
const ControllerEmu::AnalogStick::StateData right_stick_data = m_right_stick->GetState();
const ControllerEmu::AnalogStick::StateData right_stick_data =
m_right_stick->GetState(m_input_override_function);
const u8 x = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.x * RIGHT_STICK_RADIUS));
const u8 y = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.y * RIGHT_STICK_RADIUS));
@ -135,19 +137,20 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// triggers
{
ControlState trigs[2] = {0, 0};
m_triggers->GetState(&buttons, classic_trigger_bitmasks.data(), trigs);
ControlState triggers[2] = {0, 0};
m_triggers->GetState(&buttons, classic_trigger_bitmasks.data(), triggers,
m_input_override_function);
const u8 lt = static_cast<u8>(trigs[0] * TRIGGER_RANGE);
const u8 rt = static_cast<u8>(trigs[1] * TRIGGER_RANGE);
const u8 lt = static_cast<u8>(triggers[0] * TRIGGER_RANGE);
const u8 rt = static_cast<u8>(triggers[1] * TRIGGER_RANGE);
classic_data.SetLeftTrigger(lt);
classic_data.SetRightTrigger(rt);
}
// buttons and dpad
m_buttons->GetState(&buttons, classic_button_bitmasks.data());
m_dpad->GetState(&buttons, classic_dpad_bitmasks.data());
m_buttons->GetState(&buttons, classic_button_bitmasks.data(), m_input_override_function);
m_dpad->GetState(&buttons, classic_dpad_bitmasks.data(), m_input_override_function);
classic_data.SetButtons(buttons);

View File

@ -47,7 +47,7 @@ void DrawsomeTablet::BuildDesiredExtensionState(DesiredExtensionState* target_st
constexpr double CENTER_X = (MAX_X + MIN_X) / 2.0;
constexpr double CENTER_Y = (MAX_Y + MIN_Y) / 2.0;
const auto stylus_state = m_stylus->GetState();
const auto stylus_state = m_stylus->GetState(m_input_override_function);
const auto stylus_x = u16(std::lround(CENTER_X + stylus_state.x * (MAX_X - CENTER_X)));
const auto stylus_y = u16(std::lround(CENTER_Y + stylus_state.y * (MAX_Y - CENTER_Y)));
@ -74,7 +74,7 @@ void DrawsomeTablet::BuildDesiredExtensionState(DesiredExtensionState* target_st
// Pressure (0 - 0x7ff):
constexpr u16 MAX_PRESSURE = 0x7ff;
const auto touch_state = m_touch->GetState();
const auto touch_state = m_touch->GetState(m_input_override_function);
const auto pressure = u16(std::lround(touch_state.data[0] * MAX_PRESSURE));
tablet_data.pressure1 = u8(pressure);

View File

@ -84,17 +84,18 @@ void Drums::BuildDesiredExtensionState(DesiredExtensionState* target_state)
DesiredState& state = target_state->data.emplace<DesiredState>();
{
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
state.stick_x = MapFloat(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX);
state.stick_y = MapFloat(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX);
}
state.buttons = 0;
m_buttons->GetState(&state.buttons, drum_button_bitmasks.data());
m_buttons->GetState(&state.buttons, drum_button_bitmasks.data(), m_input_override_function);
state.drum_pads = 0;
m_pads->GetState(&state.drum_pads, drum_pad_bitmasks.data());
m_pads->GetState(&state.drum_pads, drum_pad_bitmasks.data(), m_input_override_function);
state.softness = u8(7 - std::lround(m_hit_strength_setting.GetValue() * 7 / 100));
}

View File

@ -101,7 +101,8 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// stick
{
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
guitar_data.sx = static_cast<u8>((stick_state.x * STICK_RADIUS) + STICK_CENTER);
guitar_data.sy = static_cast<u8>((stick_state.y * STICK_RADIUS) + STICK_CENTER);
@ -111,7 +112,8 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
if (m_slider_bar->controls[0]->control_ref->BoundCount() &&
m_slider_bar->controls[1]->control_ref->BoundCount())
{
const ControllerEmu::Slider::StateData slider_data = m_slider_bar->GetState();
const ControllerEmu::Slider::StateData slider_data =
m_slider_bar->GetState(m_input_override_function);
guitar_data.sb = s_slider_bar_control_codes.lower_bound(slider_data.value)->second;
}
@ -122,17 +124,18 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
}
// whammy bar
const ControllerEmu::Triggers::StateData whammy_state = m_whammy->GetState();
const ControllerEmu::Triggers::StateData whammy_state =
m_whammy->GetState(m_input_override_function);
guitar_data.whammy = static_cast<u8>(whammy_state.data[0] * 0x1F);
// buttons
m_buttons->GetState(&guitar_data.bt, guitar_button_bitmasks.data());
m_buttons->GetState(&guitar_data.bt, guitar_button_bitmasks.data(), m_input_override_function);
// frets
m_frets->GetState(&guitar_data.bt, guitar_fret_bitmasks.data());
m_frets->GetState(&guitar_data.bt, guitar_fret_bitmasks.data(), m_input_override_function);
// strum
m_strum->GetState(&guitar_data.bt, guitar_strum_bitmasks.data());
m_strum->GetState(&guitar_data.bt, guitar_strum_bitmasks.data(), m_input_override_function);
// flip button bits
guitar_data.bt ^= 0xFFFF;

View File

@ -67,29 +67,34 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
DataFormat nc_data = {};
// stick
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
bool override_occurred = false;
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function, &override_occurred);
nc_data.jx = u8(STICK_CENTER + stick_state.x * STICK_RADIUS);
nc_data.jy = u8(STICK_CENTER + stick_state.y * STICK_RADIUS);
// Some terribly coded games check whether to move with a check like
//
// if (x != 0 && y != 0)
// do_movement(x, y);
//
// With keyboard controls, these games break if you simply hit
// of the axes. Adjust this if you're hitting one of the axes so that
// we slightly tweak the other axis.
if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER)
if (!override_occurred)
{
if (nc_data.jx == STICK_CENTER)
++nc_data.jx;
if (nc_data.jy == STICK_CENTER)
++nc_data.jy;
// Some terribly coded games check whether to move with a check like
//
// if (x != 0 && y != 0)
// do_movement(x, y);
//
// With keyboard controls, these games break if you simply hit one
// of the axes. Adjust this if you're hitting one of the axes so that
// we slightly tweak the other axis.
if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER)
{
if (nc_data.jx == STICK_CENTER)
++nc_data.jx;
if (nc_data.jy == STICK_CENTER)
++nc_data.jy;
}
}
// buttons
u8 buttons = 0;
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data());
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data(), m_input_override_function);
nc_data.SetButtons(buttons);
// Acceleration data:
@ -108,6 +113,8 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// shake
accel += m_shake_state.acceleration;
accel = Wiimote::OverrideVec3(m_imu_accelerometer, accel, m_input_override_function);
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
const auto acc = ConvertAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
nc_data.SetAccel(acc.value);

View File

@ -54,8 +54,8 @@ void TaTaCon::BuildDesiredExtensionState(DesiredExtensionState* target_state)
{
DataFormat tatacon_data = {};
m_center->GetState(&tatacon_data.state, center_bitmasks.data());
m_rim->GetState(&tatacon_data.state, rim_bitmasks.data());
m_center->GetState(&tatacon_data.state, center_bitmasks.data(), m_input_override_function);
m_rim->GetState(&tatacon_data.state, rim_bitmasks.data(), m_input_override_function);
// Flip button bits.
tatacon_data.state ^= 0xff;

View File

@ -86,7 +86,8 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// stick
{
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
tt_data.sx = static_cast<u8>((stick_state.x * STICK_RADIUS) + STICK_CENTER);
tt_data.sy = static_cast<u8>((stick_state.y * STICK_RADIUS) + STICK_CENTER);
@ -94,7 +95,7 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// left table
{
const ControllerEmu::Slider::StateData lt = m_left_table->GetState();
const ControllerEmu::Slider::StateData lt = m_left_table->GetState(m_input_override_function);
const s8 tt = static_cast<s8>(lt.value * TABLE_RANGE);
tt_data.ltable1 = tt;
@ -103,7 +104,7 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// right table
{
const ControllerEmu::Slider::StateData rt = m_right_table->GetState();
const ControllerEmu::Slider::StateData rt = m_right_table->GetState(m_input_override_function);
const s8 tt = static_cast<s8>(rt.value * TABLE_RANGE);
tt_data.rtable1 = tt;
@ -114,7 +115,7 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// effect dial
{
const auto dial_state = m_effect_dial->GetState();
const auto dial_state = m_effect_dial->GetState(m_input_override_function);
const u8 dial = static_cast<u8>(dial_state.value * EFFECT_DIAL_RANGE) + EFFECT_DIAL_CENTER;
tt_data.dial1 = dial;
@ -123,13 +124,13 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// crossfade slider
{
const ControllerEmu::Slider::StateData cfs = m_crossfade->GetState();
const ControllerEmu::Slider::StateData cfs = m_crossfade->GetState(m_input_override_function);
tt_data.slider = static_cast<u8>((cfs.value * CROSSFADE_RANGE) + CROSSFADE_CENTER);
}
// buttons
m_buttons->GetState(&tt_data.bt, turntable_button_bitmasks.data());
m_buttons->GetState(&tt_data.bt, turntable_button_bitmasks.data(), m_input_override_function);
// flip button bits :/
tt_data.bt ^= (BUTTON_L_GREEN | BUTTON_L_RED | BUTTON_L_BLUE | BUTTON_R_GREEN | BUTTON_R_RED |

View File

@ -5,6 +5,7 @@
#include <algorithm>
#include <memory>
#include <optional>
#include <string_view>
#include <fmt/format.h>
@ -458,9 +459,10 @@ void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state)
// Fetch pressed buttons from user input.
target_state->buttons.hex = 0;
m_buttons->GetState(&target_state->buttons.hex, button_bitmasks);
m_buttons->GetState(&target_state->buttons.hex, button_bitmasks, m_input_override_function);
m_dpad->GetState(&target_state->buttons.hex,
IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks,
m_input_override_function);
// Calculate accelerometer state.
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
@ -628,9 +630,6 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
std::fill_n(ext_data, ext_size, u8(0xff));
}
}
Movie::CallWiiInputManip(rpt_builder, m_bt_device_index, m_active_extension,
GetExtensionEncryptionKey());
}
Movie::CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension,
@ -651,8 +650,9 @@ ButtonData Wiimote::GetCurrentlyPressedButtons()
const auto lock = GetStateLock();
ButtonData buttons{};
m_buttons->GetState(&buttons.hex, button_bitmasks);
m_dpad->GetState(&buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
m_buttons->GetState(&buttons.hex, button_bitmasks, m_input_override_function);
m_dpad->GetState(&buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks,
m_input_override_function);
return buttons;
}
@ -789,7 +789,7 @@ void Wiimote::StepDynamics()
{
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
EmulatePoint(&m_point_state, m_ir, 1.f / ::Wiimote::UPDATE_FREQ);
EmulatePoint(&m_point_state, m_ir, m_input_override_function, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateIMUCursor(&m_imu_cursor_state, m_imu_ir, m_imu_accelerometer, m_imu_gyroscope,
1.f / ::Wiimote::UPDATE_FREQ);
@ -831,20 +831,87 @@ Common::Quaternion Wiimote::GetOrientation() const
Common::Quaternion::RotateX(float(MathUtil::TAU / 4 * IsUpright()));
}
std::optional<Common::Vec3> Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group,
std::optional<Common::Vec3> optional_vec) const
{
bool has_value = optional_vec.has_value();
Common::Vec3 vec = has_value ? *optional_vec : Common::Vec3{};
if (m_input_override_function)
{
if (const std::optional<ControlState> x_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE, vec.x))
{
has_value = true;
vec.x = *x_override;
}
if (const std::optional<ControlState> y_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE, vec.y))
{
has_value = true;
vec.y = *y_override;
}
if (const std::optional<ControlState> z_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE, vec.z))
{
has_value = true;
vec.z = *z_override;
}
}
return has_value ? std::make_optional(vec) : std::nullopt;
}
Common::Vec3 Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group,
Common::Vec3 vec) const
{
return OverrideVec3(control_group, vec, m_input_override_function);
}
Common::Vec3
Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
const ControllerEmu::InputOverrideFunction& input_override_function)
{
if (input_override_function)
{
if (const std::optional<ControlState> x_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE, vec.x))
{
vec.x = *x_override;
}
if (const std::optional<ControlState> y_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE, vec.y))
{
vec.y = *y_override;
}
if (const std::optional<ControlState> z_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE, vec.z))
{
vec.z = *z_override;
}
}
return vec;
}
Common::Vec3 Wiimote::GetTotalAcceleration() const
{
if (const auto accel = m_imu_accelerometer->GetState())
return GetAcceleration(*accel);
const Common::Vec3 default_accel = Common::Vec3(0, 0, float(GRAVITY_ACCELERATION));
const Common::Vec3 accel = m_imu_accelerometer->GetState().value_or(default_accel);
return GetAcceleration();
return OverrideVec3(m_imu_accelerometer, GetAcceleration(accel));
}
Common::Vec3 Wiimote::GetTotalAngularVelocity() const
{
if (const auto ang_vel = m_imu_gyroscope->GetState())
return GetAngularVelocity(*ang_vel);
const Common::Vec3 default_ang_vel = {};
const Common::Vec3 ang_vel = m_imu_gyroscope->GetState().value_or(default_ang_vel);
return GetAngularVelocity();
return OverrideVec3(m_imu_gyroscope, GetAngularVelocity(ang_vel));
}
Common::Matrix44 Wiimote::GetTotalTransformation() const

View File

@ -5,6 +5,7 @@
#include <array>
#include <numeric>
#include <optional>
#include <string>
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
@ -146,6 +147,10 @@ public:
// Active extension number is exposed for TAS.
ExtensionNumber GetActiveExtensionNumber() const;
static Common::Vec3
OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
const ControllerEmu::InputOverrideFunction& input_override_function);
private:
// Used only for error generation:
static constexpr u8 EEPROM_I2C_ADDR = 0x50;
@ -161,11 +166,10 @@ private:
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state);
// Returns simulated accelerometer data in m/s^2.
Common::Vec3 GetAcceleration(
Common::Vec3 extra_acceleration = Common::Vec3(0, 0, float(GRAVITY_ACCELERATION))) const;
Common::Vec3 GetAcceleration(Common::Vec3 extra_acceleration) const;
// Returns simulated gyroscope data in radians/s.
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity = {}) const;
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity) const;
// Returns the transformation of the world around the wiimote.
// Used for simulating camera data and for rotating acceleration data.
@ -176,6 +180,10 @@ private:
// Returns the world rotation from the effects of sideways/upright settings.
Common::Quaternion GetOrientation() const;
std::optional<Common::Vec3> OverrideVec3(const ControllerEmu::ControlGroup* control_group,
std::optional<Common::Vec3> optional_vec) const;
Common::Vec3 OverrideVec3(const ControllerEmu::ControlGroup* control_group,
Common::Vec3 vec) const;
Common::Vec3 GetTotalAcceleration() const;
Common::Vec3 GetTotalAngularVelocity() const;
Common::Matrix44 GetTotalTransformation() const;

View File

@ -4,6 +4,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h"
#include <cmath>
#include <optional>
#include "Common/Common.h"
#include "Common/MathUtil.h"
@ -48,6 +49,34 @@ AnalogStick::StateData AnalogStick::GetState() const
return GetReshapableState(true);
}
AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func) const
{
bool override_occurred = false;
return GetState(override_func, &override_occurred);
}
AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func,
bool* override_occurred) const
{
StateData state = GetState();
if (!override_func)
return state;
if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x))
{
state.x = *x_override;
*override_occurred = true;
}
if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y))
{
state.y = *y_override;
*override_occurred = true;
}
return state;
}
ControlState AnalogStick::GetGateRadiusAtAngle(double ang) const
{
return m_stick_gate->GetRadiusAtAngle(ang);

View File

@ -21,6 +21,8 @@ public:
ControlState GetGateRadiusAtAngle(double ang) const override;
StateData GetState() const;
StateData GetState(const InputOverrideFunction& override_func) const;
StateData GetState(const InputOverrideFunction& override_func, bool* override_occurred) const;
private:
Control* GetModifierInput() const override;

View File

@ -3,6 +3,7 @@
#pragma once
#include <cmath>
#include <string>
#include "InputCommon/ControlReference/ControlReference.h"
@ -24,5 +25,21 @@ public:
for (auto& control : controls)
*buttons |= *(bitmasks++) * control->GetState<bool>();
}
template <typename C>
void GetState(C* const buttons, const C* bitmasks,
const InputOverrideFunction& override_func) const
{
if (!override_func)
return GetState(buttons, bitmasks);
for (auto& control : controls)
{
ControlState state = control->GetState();
if (std::optional<ControlState> state_override = override_func(name, control->name, state))
state = *state_override;
*buttons |= *(bitmasks++) * (std::lround(state) > 0);
}
}
};
} // namespace ControllerEmu

View File

@ -5,14 +5,18 @@
#include <algorithm>
#include <cmath>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/IniFile.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
namespace ControllerEmu
{
@ -27,6 +31,9 @@ class NumericSetting;
template <typename T>
class SettingValue;
using InputOverrideFunction = std::function<std::optional<ControlState>(
const std::string_view group_name, const std::string_view control_name, ControlState state)>;
enum class GroupType
{
Other,

View File

@ -82,15 +82,28 @@ ControlState Cursor::GetGateRadiusAtAngle(double ang) const
Cursor::StateData Cursor::GetState(const bool adjusted)
{
if (!adjusted)
{
const auto raw_input = GetReshapableState(false);
const ReshapeData input = GetReshapableState(adjusted);
const StateData state = adjusted ? UpdateState(input) : StateData{input.x, input.y};
return state;
}
return {raw_input.x, raw_input.y};
}
Cursor::StateData Cursor::GetState(const bool adjusted,
const ControllerEmu::InputOverrideFunction& override_func)
{
StateData state = GetState(adjusted);
if (!override_func)
return state;
const auto input = GetReshapableState(true);
if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x))
state.x = *x_override;
if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y))
state.y = *y_override;
return state;
}
Cursor::StateData Cursor::UpdateState(Cursor::ReshapeData input)
{
// TODO: Using system time is ugly.
// Kill this after state is moved into wiimote rather than this class.
const auto now = Clock::now();

View File

@ -29,6 +29,7 @@ public:
// Modifies the state
StateData GetState(bool adjusted);
StateData GetState(bool adjusted, const ControllerEmu::InputOverrideFunction& override_func);
// Yaw movement in radians.
ControlState GetTotalYaw() const;
@ -40,6 +41,8 @@ public:
ControlState GetVerticalOffset() const;
private:
Cursor::StateData UpdateState(Cursor::ReshapeData input);
// This is used to reduce the cursor speed for relative input
// to something that makes sense with the default range.
static constexpr double STEP_PER_SEC = 0.01 * 200;

View File

@ -4,6 +4,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <memory>
#include <string>
@ -63,6 +64,53 @@ void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlSta
}
}
void MixedTriggers::GetState(u16* digital, const u16* bitmasks, ControlState* analog,
const InputOverrideFunction& override_func, bool adjusted) const
{
if (!override_func)
return GetState(digital, bitmasks, analog, adjusted);
const ControlState threshold = GetThreshold();
ControlState deadzone = GetDeadzone();
// Return raw values. (used in UI)
if (!adjusted)
{
deadzone = 0.0;
}
const int trigger_count = int(controls.size() / 2);
for (int i = 0; i != trigger_count; ++i)
{
bool button_bool = false;
const ControlState button_value = ApplyDeadzone(controls[i]->GetState(), deadzone);
ControlState analog_value = ApplyDeadzone(controls[trigger_count + i]->GetState(), deadzone);
// Apply threshold:
if (button_value > threshold)
{
analog_value = 1.0;
button_bool = true;
}
if (const std::optional<ControlState> button_override =
override_func(name, controls[i]->name, static_cast<ControlState>(button_bool)))
{
button_bool = std::lround(*button_override) > 0;
}
if (const std::optional<ControlState> analog_override =
override_func(name, controls[trigger_count + i]->name, analog_value))
{
analog_value = *analog_override;
}
if (button_bool)
*digital |= bitmasks[i];
analog[i] = std::min(analog_value, 1.0);
}
}
ControlState MixedTriggers::GetDeadzone() const
{
return m_deadzone_setting.GetValue() / 100;

View File

@ -17,6 +17,8 @@ public:
void GetState(u16* digital, const u16* bitmasks, ControlState* analog,
bool adjusted = true) const;
void GetState(u16* digital, const u16* bitmasks, ControlState* analog,
const InputOverrideFunction& override_func, bool adjusted = true) const;
ControlState GetDeadzone() const;
ControlState GetThreshold() const;

View File

@ -35,4 +35,21 @@ Slider::StateData Slider::GetState() const
return {std::clamp(ApplyDeadzone(state, deadzone), -1.0, 1.0)};
}
Slider::StateData Slider::GetState(const InputOverrideFunction& override_func) const
{
if (!override_func)
return GetState();
const ControlState deadzone = m_deadzone_setting.GetValue() / 100;
ControlState state = controls[1]->GetState() - controls[0]->GetState();
state = ApplyDeadzone(state, deadzone);
if (std::optional<ControlState> state_override = override_func(name, X_INPUT_OVERRIDE, state))
state = *state_override;
return {std::clamp(state, -1.0, 1.0)};
}
} // namespace ControllerEmu

View File

@ -23,6 +23,9 @@ public:
explicit Slider(const std::string& name_);
StateData GetState() const;
StateData GetState(const InputOverrideFunction& override_func) const;
static constexpr const char* X_INPUT_OVERRIDE = "X";
private:
SettingValue<double> m_deadzone_setting;

View File

@ -6,6 +6,7 @@
#include <algorithm>
#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include "Common/Common.h"
@ -31,4 +32,25 @@ Triggers::StateData Triggers::GetState() const
return result;
}
Triggers::StateData Triggers::GetState(const InputOverrideFunction& override_func) const
{
if (!override_func)
return GetState();
const size_t trigger_count = controls.size();
const ControlState deadzone = m_deadzone_setting.GetValue() / 100;
StateData result(trigger_count);
for (size_t i = 0; i < trigger_count; ++i)
{
ControlState state = ApplyDeadzone(controls[i]->GetState(), deadzone);
if (std::optional<ControlState> state_override = override_func(name, controls[i]->name, state))
state = *state_override;
result.data[i] = std::min(state, 1.0);
}
return result;
}
} // namespace ControllerEmu

View File

@ -26,6 +26,7 @@ public:
explicit Triggers(const std::string& name);
StateData GetState() const;
StateData GetState(const InputOverrideFunction& override_func) const;
private:
SettingValue<double> m_deadzone_setting;

View File

@ -6,6 +6,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include "Common/IniFile.h"
@ -176,4 +177,15 @@ void EmulatedController::LoadDefaults(const ControllerInterface& ciface)
SetDefaultDevice(default_device_string);
}
}
void EmulatedController::SetInputOverrideFunction(InputOverrideFunction override_func)
{
m_input_override_function = std::move(override_func);
}
void EmulatedController::ClearInputOverrideFunction()
{
m_input_override_function = {};
}
} // namespace ControllerEmu

View File

@ -15,6 +15,7 @@
#include "Common/IniFile.h"
#include "Common/MathUtil.h"
#include "InputCommon/ControlReference/ExpressionParser.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
class ControllerInterface;
@ -184,6 +185,9 @@ public:
void SetDefaultDevice(const std::string& device);
void SetDefaultDevice(ciface::Core::DeviceQualifier devq);
void SetInputOverrideFunction(InputOverrideFunction override_func);
void ClearInputOverrideFunction();
void UpdateReferences(const ControllerInterface& devi);
void UpdateSingleControlReference(const ControllerInterface& devi, ControlReference* ref);
@ -228,6 +232,8 @@ protected:
// so theirs won't be used (and thus shouldn't even exist).
ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars;
InputOverrideFunction m_input_override_function;
void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env);
private:

View File

@ -106,6 +106,10 @@ public:
const ReshapeData& GetCenter() const;
void SetCenter(ReshapeData center);
static constexpr const char* X_INPUT_OVERRIDE = "X";
static constexpr const char* Y_INPUT_OVERRIDE = "Y";
static constexpr const char* Z_INPUT_OVERRIDE = "Z";
protected:
ReshapeData Reshape(ControlState x, ControlState y, ControlState modifier = 0.0,
ControlState clamp = 1.0) const;