Input: Add config lerp factor for buttons and triggers

Adds new lerp factors to the keyboard pad handler In order to simulate triggers and analog buttons.
See "Analog Button Lerp Factor" and "Trigger Lerp Factor" in the yml in InputConfigs/Keyboard/.
Values Range from 0-100 as before, where 100 is instant press and 0 is never.

Currently I'm not planning any GUI element for this.
This commit is contained in:
Megamouse 2020-02-23 11:48:45 +01:00
parent 64ed2f1151
commit 7b49249f5f
4 changed files with 144 additions and 45 deletions

View File

@ -112,11 +112,36 @@ struct Button
u16 m_value = 0;
bool m_pressed = false;
u16 m_actual_value = 0; // only used in keyboard_pad_handler
bool m_analog = false; // only used in keyboard_pad_handler
bool m_trigger = false; // only used in keyboard_pad_handler
Button(u32 offset, u32 keyCode, u32 outKeyCode)
: m_offset(offset)
, m_keyCode(keyCode)
, m_outKeyCode(outKeyCode)
{
if (offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
{
if (outKeyCode == CELL_PAD_CTRL_LEFT || outKeyCode == CELL_PAD_CTRL_RIGHT ||
outKeyCode == CELL_PAD_CTRL_UP || outKeyCode == CELL_PAD_CTRL_DOWN)
{
m_analog = true;
}
}
else if (offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
{
if (outKeyCode == CELL_PAD_CTRL_CROSS || outKeyCode == CELL_PAD_CTRL_CIRCLE ||
outKeyCode == CELL_PAD_CTRL_SQUARE || outKeyCode == CELL_PAD_CTRL_TRIANGLE ||
outKeyCode == CELL_PAD_CTRL_L1 || outKeyCode == CELL_PAD_CTRL_R1)
{
m_analog = true;
}
else if (outKeyCode == CELL_PAD_CTRL_L2 || outKeyCode == CELL_PAD_CTRL_R2)
{
m_trigger = true;
}
}
}
};

View File

@ -99,6 +99,8 @@ struct pad_config final : cfg::node
cfg::_int<0, 100> l_stick_lerp_factor{ this, "Left Stick Lerp Factor", 100 };
cfg::_int<0, 100> r_stick_lerp_factor{ this, "Right Stick Lerp Factor", 100 };
cfg::_int<0, 100> analog_lerp_factor{ this, "Analog Button Lerp Factor", 100 };
cfg::_int<0, 100> trigger_lerp_factor{ this, "Trigger Lerp Factor", 100 };
cfg::_int<0, 5> device_class_type{ this, "Device Class Type", 0 };

View File

@ -75,8 +75,25 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value)
if (button.m_keyCode != code)
continue;
button.m_pressed = pressed;
button.m_value = pressed ? value : 0;
button.m_actual_value = pressed ? value : 0;
bool update_button = true;
// to get the fastest response time possible we don't wanna use any lerp with factor 1
if (button.m_analog)
{
update_button = m_analog_lerp_factor >= 1.0f;
}
else if (button.m_trigger)
{
update_button = m_trigger_lerp_factor >= 1.0f;
}
if (update_button)
{
button.m_value = pressed ? value : 0;
button.m_pressed = pressed;
}
}
for (int i = 0; i < static_cast<int>(pad->m_sticks.size()); i++)
@ -576,6 +593,8 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::
m_multi_y = p_profile->mouse_acceleration_y / 100.0;
m_l_stick_lerp_factor = p_profile->l_stick_lerp_factor / 100.0f;
m_r_stick_lerp_factor = p_profile->r_stick_lerp_factor / 100.0f;
m_analog_lerp_factor = p_profile->analog_lerp_factor / 100.0f;
m_trigger_lerp_factor = p_profile->trigger_lerp_factor / 100.0f;
auto find_key = [&](const cfg::string& name)
{
@ -635,6 +654,63 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::
void keyboard_pad_handler::ThreadProc()
{
static const double mouse_interval = 30.0;
static const double stick_interval = 10.0;
static const double button_interval = 10.0;
const auto now = std::chrono::steady_clock::now();
const double elapsed_left = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_left).count() / 1000.0;
const double elapsed_right = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_right).count() / 1000.0;
const double elapsed_up = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_up).count() / 1000.0;
const double elapsed_down = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_down).count() / 1000.0;
const double elapsed_stick = std::chrono::duration_cast<std::chrono::microseconds>(now - m_stick_time).count() / 1000.0;
const double elapsed_button = std::chrono::duration_cast<std::chrono::microseconds>(now - m_button_time).count() / 1000.0;
const bool update_sticks = elapsed_stick > stick_interval;
const bool update_buttons = elapsed_button > button_interval;
if (update_sticks)
{
m_stick_time = now;
}
if (update_buttons)
{
m_button_time = now;
}
// roughly 1-2 frames to process the next mouse move
if (elapsed_left > mouse_interval)
{
Key(mouse::move_left, false);
m_last_mouse_move_left = now;
}
if (elapsed_right > mouse_interval)
{
Key(mouse::move_right, false);
m_last_mouse_move_right = now;
}
if (elapsed_up > mouse_interval)
{
Key(mouse::move_up, false);
m_last_mouse_move_up = now;
}
if (elapsed_down > mouse_interval)
{
Key(mouse::move_down, false);
m_last_mouse_move_down = now;
}
const auto get_lerped = [](f32 v0, f32 v1, f32 lerp_factor)
{
// linear interpolation from the current value v0 to the desired value v1
const f32 res = std::lerp(v0, v1, lerp_factor);
// round to the correct direction to prevent sticky values on small factors
return (v0 <= v1) ? std::ceil(res) : std::floor(res);
};
for (uint i = 0; i < bindings.size(); i++)
{
if (last_connection_status[i] == false)
@ -646,41 +722,7 @@ void keyboard_pad_handler::ThreadProc()
}
else
{
static std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
now = std::chrono::steady_clock::now();
const double elapsed_left = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_left).count() / 1000.0;
const double elapsed_right = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_right).count() / 1000.0;
const double elapsed_up = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_up).count() / 1000.0;
const double elapsed_down = std::chrono::duration_cast<std::chrono::microseconds>(now - m_last_mouse_move_down).count() / 1000.0;
const double elapsed_stick = std::chrono::duration_cast<std::chrono::microseconds>(now - m_stick_time).count() / 1000.0;
const double mouse_interval = 30.0;
const double stick_interval = 10.0;
// roughly 1-2 frames to process the next mouse move
if (elapsed_left > mouse_interval)
{
Key(mouse::move_left, false);
m_last_mouse_move_left = now;
}
if (elapsed_right > mouse_interval)
{
Key(mouse::move_right, false);
m_last_mouse_move_right = now;
}
if (elapsed_up > mouse_interval)
{
Key(mouse::move_up, false);
m_last_mouse_move_up = now;
}
if (elapsed_down > mouse_interval)
{
Key(mouse::move_down, false);
m_last_mouse_move_down = now;
}
if (elapsed_stick > stick_interval)
if (update_sticks)
{
for (int j = 0; j < static_cast<int>(bindings[i]->m_sticks.size()); j++)
{
@ -691,25 +733,50 @@ void keyboard_pad_handler::ThreadProc()
{
const f32 v0 = static_cast<f32>(bindings[i]->m_sticks[j].m_value);
const f32 v1 = static_cast<f32>(m_stick_val[j]);
// linear interpolation from the current stick value v0 to the desired stick value v1
f32 res = std::lerp(v0, v1, stick_lerp_factor);
// round to the correct direction to prevent sticky sticks on small factors
res = (v0 <= v1) ? std::ceil(res) : std::floor(res);
const f32 res = get_lerped(v0, v1, stick_lerp_factor);
bindings[i]->m_sticks[j].m_value = static_cast<u16>(res);
}
}
}
m_stick_time = now;
if (update_buttons)
{
for (auto& button : bindings[i]->m_buttons)
{
if (button.m_analog)
{
// we already applied the following values on keypress if we used factor 1
if (m_analog_lerp_factor < 1.0f)
{
const f32 v0 = static_cast<f32>(button.m_value);
const f32 v1 = static_cast<f32>(button.m_actual_value);
const f32 res = get_lerped(v0, v1, m_analog_lerp_factor);
button.m_value = static_cast<u16>(res);
button.m_pressed = button.m_value > 0;
}
}
else if (button.m_trigger)
{
// we already applied the following values on keypress if we used factor 1
if (m_trigger_lerp_factor < 1.0f)
{
const f32 v0 = static_cast<f32>(button.m_value);
const f32 v1 = static_cast<f32>(button.m_actual_value);
const f32 res = get_lerped(v0, v1, m_trigger_lerp_factor);
button.m_value = static_cast<u16>(res);
button.m_pressed = button.m_value > 0;
}
}
}
}
}
}
// Releases the wheel buttons 0,1 sec after they've been triggered
// Next activation is set to distant future to avoid activating this on every proc
const auto now = std::chrono::steady_clock::now();
const auto update_threshold = now - std::chrono::milliseconds(100);
const auto distant_future = now + std::chrono::hours(24);
if (update_threshold >= m_last_wheel_move_up)

View File

@ -101,6 +101,11 @@ private:
QWindow* m_target = nullptr;
std::vector<std::shared_ptr<Pad>> bindings;
// Button Movements
std::chrono::steady_clock::time_point m_button_time;
f32 m_analog_lerp_factor = 1.0f;
f32 m_trigger_lerp_factor = 1.0f;
// Stick Movements
std::chrono::steady_clock::time_point m_stick_time;
f32 m_l_stick_lerp_factor = 1.0f;