@@ -12,7 +12,6 @@
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Core/Config/WiimoteInputSettings.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

@@ -50,24 +49,10 @@ Nunchuk::Nunchuk() : EncryptedExtension(_trans("Nunchuk"))
// tilt
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));

// shake
groups.emplace_back(m_shake = new ControllerEmu::Buttons(_trans("Shake")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("X")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Y")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z")));

groups.emplace_back(m_shake_soft = new ControllerEmu::Buttons("ShakeSoft"));
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));

groups.emplace_back(m_shake_hard = new ControllerEmu::Buttons("ShakeHard"));
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
// Shake
// Inverse the default intensity so shake is opposite that of wiimote.
// This is needed by DKCR for proper shake action detection.
groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake"), -1));
}

void Nunchuk::Update()
@@ -104,6 +89,7 @@ void Nunchuk::Update()
// Acceleration data:
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);

const auto transformation =
GetRotationalMatrix(-m_tilt_state.angle) * GetRotationalMatrix(-m_swing_state.angle);
@@ -112,12 +98,7 @@ void Nunchuk::Update()
Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)));

// shake
accel += EmulateShake(m_shake, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM),
m_shake_step.data());
accel += EmulateShake(m_shake_soft, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT),
m_shake_soft_step.data());
accel += EmulateShake(m_shake_hard, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_HARD),
m_shake_hard_step.data());
accel += m_shake_state.acceleration;

// 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);
@@ -203,6 +184,7 @@ void Nunchuk::DoState(PointerWrap& p)

p.Do(m_swing_state);
p.Do(m_tilt_state);
p.Do(m_shake_state);
}

void Nunchuk::LoadDefaults(const ControllerInterface& ciface)
@@ -227,5 +209,9 @@ void Nunchuk::LoadDefaults(const ControllerInterface& ciface)
m_buttons->SetControlExpression(0, "Control_L"); // C
m_buttons->SetControlExpression(1, "Shift_L"); // Z
#endif

// Shake
for (int i = 0; i < 3; ++i)
m_shake->SetControlExpression(i, "Click 2");
}
} // namespace WiimoteEmu
@@ -101,23 +101,14 @@ class Nunchuk : public EncryptedExtension

private:
ControllerEmu::Tilt* m_tilt;

ControllerEmu::Force* m_swing;

ControllerEmu::Buttons* m_shake;
ControllerEmu::Buttons* m_shake_soft;
ControllerEmu::Buttons* m_shake_hard;

ControllerEmu::Shake* m_shake;
ControllerEmu::Buttons* m_buttons;
ControllerEmu::AnalogStick* m_stick;

// Dynamics:
MotionState m_swing_state;
RotationalState m_tilt_state;

// TODO: kill
std::array<u8, 3> m_shake_step{};
std::array<u8, 3> m_shake_soft_step{};
std::array<u8, 3> m_shake_hard_step{};
PositionalState m_shake_state;
};
} // namespace WiimoteEmu
@@ -17,7 +17,6 @@
#include "Common/MsgHandler.h"

#include "Core/Config/SYSCONFSettings.h"
#include "Core/Config/WiimoteInputSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/Wiimote.h"
@@ -130,11 +129,7 @@ void Wiimote::Reset()
// Dynamics:
m_swing_state = {};
m_tilt_state = {};

m_shake_step = {};
m_shake_soft_step = {};
m_shake_hard_step = {};
m_shake_dynamic_data = {};
m_shake_state = {};
}

Wiimote::Wiimote(const unsigned int index) : m_index(index)
@@ -159,31 +154,7 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index)
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));

// shake
groups.emplace_back(m_shake = new ControllerEmu::Buttons(_trans("Shake")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("X")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Y")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z")));

groups.emplace_back(m_shake_soft = new ControllerEmu::Buttons("ShakeSoft"));
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));

groups.emplace_back(m_shake_hard = new ControllerEmu::Buttons("ShakeHard"));
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));

groups.emplace_back(m_shake_dynamic = new ControllerEmu::Buttons("Shake Dynamic"));
m_shake_dynamic->controls.emplace_back(
new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
m_shake_dynamic->controls.emplace_back(
new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
m_shake_dynamic->controls.emplace_back(
new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake")));

// extension
groups.emplace_back(m_attachments = new ControllerEmu::Attachments(_trans("Extension")));
@@ -687,6 +658,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);
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);

// TODO: Move cursor state out of ControllerEmu::Cursor
// const auto cursor_mtx = EmulateCursorMovement(m_ir);
@@ -710,34 +682,18 @@ Common::Vec3 Wiimote::GetAcceleration()
GetTransformation().Transform(
m_swing_state.acceleration + Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)), 0);

DynamicConfiguration shake_config;
shake_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT);
shake_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM);
shake_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD);
shake_config.frames_needed_for_high_intensity =
Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD);
shake_config.frames_needed_for_low_intensity =
Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT);
shake_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH);

accel += EmulateShake(m_shake, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM),
m_shake_step.data());
accel += EmulateShake(m_shake_soft, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT),
m_shake_soft_step.data());
accel += EmulateShake(m_shake_hard, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD),
m_shake_hard_step.data());
accel += EmulateDynamicShake(m_shake_dynamic_data, m_shake_dynamic, shake_config,
m_shake_dynamic_step.data());
accel += m_shake_state.acceleration;

return accel;
}

Common::Matrix44 Wiimote::GetTransformation() const
{
// Includes positional and rotational effects of:
// IR, Swing, Tilt
// IR, Swing, Tilt, Shake

return Common::Matrix44::FromMatrix33(GetRotationalMatrix(-m_tilt_state.angle) *
return Common::Matrix44::Translate(-m_shake_state.position) *
Common::Matrix44::FromMatrix33(GetRotationalMatrix(-m_tilt_state.angle) *
GetRotationalMatrix(-m_swing_state.angle)) *
EmulateCursorMovement(m_ir) * Common::Matrix44::Translate(-m_swing_state.position);
}
@@ -222,10 +222,7 @@ class Wiimote : public ControllerEmu::EmulatedController
// Control groups for user input:
ControllerEmu::Buttons* m_buttons;
ControllerEmu::Buttons* m_dpad;
ControllerEmu::Buttons* m_shake;
ControllerEmu::Buttons* m_shake_soft;
ControllerEmu::Buttons* m_shake_hard;
ControllerEmu::Buttons* m_shake_dynamic;
ControllerEmu::Shake* m_shake;
ControllerEmu::Cursor* m_ir;
ControllerEmu::Tilt* m_tilt;
ControllerEmu::Force* m_swing;
@@ -270,12 +267,6 @@ class Wiimote : public ControllerEmu::EmulatedController
// Dynamics:
MotionState m_swing_state;
RotationalState m_tilt_state;

// TODO: kill these:
std::array<u8, 3> m_shake_step{};
std::array<u8, 3> m_shake_soft_step{};
std::array<u8, 3> m_shake_hard_step{};
std::array<u8, 3> m_shake_dynamic_step{};
DynamicData m_shake_dynamic_data;
PositionalState m_shake_state;
};
} // namespace WiimoteEmu
@@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;

// Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 106; // Last changed in PR 7984
static const u32 STATE_VERSION = 107; // Last changed in PR 7952

// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
@@ -514,6 +514,95 @@ void MappingIndicator::paintEvent(QPaintEvent*)
Settings::Instance().SetControllerStateNeeded(false);
}

ShakeMappingIndicator::ShakeMappingIndicator(ControllerEmu::Shake* group)
: MappingIndicator(group), m_shake_group(*group)
{
}

void ShakeMappingIndicator::paintEvent(QPaintEvent*)
{
Settings::Instance().SetControllerStateNeeded(true);
DrawShake();
Settings::Instance().SetControllerStateNeeded(false);
}

void ShakeMappingIndicator::DrawShake()
{
constexpr std::size_t HISTORY_COUNT = INDICATOR_UPDATE_FREQ;

WiimoteEmu::EmulateShake(&m_motion_state, &m_shake_group, 1.f / INDICATOR_UPDATE_FREQ);

constexpr float MAX_DISTANCE = 0.5f;

m_position_samples.push_front(m_motion_state.position / MAX_DISTANCE);
// This also holds the current state so +1.
if (m_position_samples.size() > HISTORY_COUNT + 1)
m_position_samples.pop_back();

// Bounding box size:
const double scale = height() / 2.5;

QPainter p(this);
p.translate(width() / 2, height() / 2);

// Bounding box.
p.setBrush(BBOX_BRUSH_COLOR);
p.setPen(BBOX_PEN_COLOR);
p.drawRect(-scale - 1, -scale - 1, scale * 2 + 1, scale * 2 + 1);

// UI y-axis is opposite that of acceleration Z.
p.scale(1.0, -1.0);

// Enable AA after drawing bounding box.
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);

// Deadzone.
p.setPen(DEADZONE_COLOR);
p.setBrush(DEADZONE_BRUSH);
p.drawRect(-scale, 0, scale * 2, m_shake_group.GetDeadzone() * scale);

// Raw input.
const auto raw_coord = m_shake_group.GetState(false);
p.setPen(Qt::NoPen);
p.setBrush(RAW_INPUT_COLOR);
for (std::size_t c = 0; c != raw_coord.data.size(); ++c)
{
p.drawEllipse(QPointF{-0.5 + c * 0.5, raw_coord.data[c]} * scale, INPUT_DOT_RADIUS,
INPUT_DOT_RADIUS);
}

// Grid line.
if (m_grid_line_position ||
std::any_of(m_position_samples.begin(), m_position_samples.end(),
[](const Common::Vec3& v) { return v.LengthSquared() != 0.0; }))
{
// Only start moving the line if there's non-zero data.
m_grid_line_position = (m_grid_line_position + 1) % HISTORY_COUNT;
}
const double grid_line_x = 1.0 - m_grid_line_position * 2.0 / HISTORY_COUNT;
p.setPen(RAW_INPUT_COLOR);
p.drawLine(QPointF{grid_line_x, -1.0} * scale, QPointF{grid_line_x, 1.0} * scale);

// Position history.
const QColor component_colors[] = {Qt::red, Qt::green, Qt::blue};
p.setBrush(Qt::NoBrush);
for (std::size_t c = 0; c != raw_coord.data.size(); ++c)
{
QPolygonF polyline;

int i = 0;
for (auto& sample : m_position_samples)
{
polyline.append(QPointF{1.0 - i * 2.0 / HISTORY_COUNT, sample.data[c]} * scale);
++i;
}

p.setPen(component_colors[c]);
p.drawPolyline(polyline);
}
}

void MappingIndicator::DrawCalibration(QPainter& p, Common::DVec2 point)
{
// TODO: Ugly magic number used in a few places in this file.
@@ -7,6 +7,8 @@
#include <QToolButton>
#include <QWidget>

#include <deque>

#include "Core/HW/WiimoteEmu/Dynamics.h"
#include "InputCommon/ControllerEmu/StickGate.h"

@@ -31,6 +33,9 @@ class MappingIndicator : public QWidget

void SetCalibrationWidget(CalibrationWidget* widget);

protected:
WiimoteEmu::MotionState m_motion_state{};

private:
void DrawCursor(ControllerEmu::Cursor& cursor);
void DrawReshapableInput(ControllerEmu::ReshapableInput& stick);
@@ -45,8 +50,21 @@ class MappingIndicator : public QWidget

ControllerEmu::ControlGroup* const m_group;
CalibrationWidget* m_calibration_widget{};
};

WiimoteEmu::MotionState m_motion_state{};
class ShakeMappingIndicator : public MappingIndicator
{
public:
explicit ShakeMappingIndicator(ControllerEmu::Shake* group);

void DrawShake();
void paintEvent(QPaintEvent*) override;

private:
std::deque<ControllerEmu::Shake::StateData> m_position_samples;
int m_grid_line_position = 0;

ControllerEmu::Shake& m_shake_group;
};

class CalibrationWidget : public QToolButton
@@ -85,7 +85,8 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
group->type == ControllerEmu::GroupType::Stick ||
group->type == ControllerEmu::GroupType::Tilt ||
group->type == ControllerEmu::GroupType::MixedTriggers ||
group->type == ControllerEmu::GroupType::Force;
group->type == ControllerEmu::GroupType::Force ||
group->type == ControllerEmu::GroupType::Shake;

const bool need_calibration = group->type == ControllerEmu::GroupType::Cursor ||
group->type == ControllerEmu::GroupType::Stick ||
@@ -129,7 +130,19 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con

if (need_indicator)
{
auto const indicator = new MappingIndicator(group);
MappingIndicator* indicator;

switch (group->type)
{
case ControllerEmu::GroupType::Shake:
indicator = new ShakeMappingIndicator(static_cast<ControllerEmu::Shake*>(group));
break;

default:
indicator = new MappingIndicator(group);
break;
}

connect(this, &MappingWidget::Update, indicator, QOverload<>::of(&MappingIndicator::update));

if (need_calibration)
@@ -4,8 +4,10 @@

#pragma once

#include <cmath>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#include "Common/CommonTypes.h"
@@ -35,7 +37,8 @@ enum class GroupType
Tilt,
Cursor,
Triggers,
Slider
Slider,
Shake,
};

class ControlGroup
@@ -64,6 +67,12 @@ class ControlGroup

void AddDeadzoneSetting(SettingValue<double>* value, double maximum_deadzone);

template <typename T>
static T ApplyDeadzone(T input, std::common_type_t<T> deadzone)
{
return std::copysign(std::max(T{0}, std::abs(input) - deadzone) / (T{1} - deadzone), input);
}

const std::string name;
const std::string ui_name;
const GroupType type;
@@ -104,8 +104,7 @@ Cursor::StateData Cursor::GetState(const bool adjusted)
const double max_z_step = STEP_Z_PER_SEC / 1000.0 * ms_since_update;

// Apply deadzone to z:
const ControlState deadzone = GetDeadzonePercentage();
z = std::copysign(std::max(0.0, std::abs(z) - deadzone) / (1.0 - deadzone), z);
z = ApplyDeadzone(z, GetDeadzonePercentage());

// Smooth out z movement:
// FYI: Not using relative input for Z.
@@ -71,8 +71,7 @@ Force::StateData Force::GetState(bool adjusted)
if (adjusted)
{
// Apply deadzone to z.
const ControlState deadzone = GetDeadzonePercentage();
z = std::copysign(std::max(0.0, std::abs(z) - deadzone) / (1.0 - deadzone), z);
z = ApplyDeadzone(z, GetDeadzonePercentage());
}

return {float(state.x), float(state.y), float(z)};
@@ -105,4 +104,69 @@ ControlState Force::GetDefaultInputRadiusAtAngle(double) const
return 1.0;
}

Shake::Shake(const std::string& name_, ControlState default_intensity_scale)
: ControlGroup(name_, name_, GroupType::Shake)
{
// i18n: Refers to a 3D axis (used when mapping motion controls)
controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("X")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Y")));
// i18n: Refers to a 3D axis (used when mapping motion controls)
controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z")));

AddDeadzoneSetting(&m_deadzone_setting, 50);

// Total travel distance in centimeters.
// Negative values can be used to reverse the initial direction of movement.
AddSetting(&m_intensity_setting,
// i18n: Refers to the intensity of shaking an emulated wiimote.
{_trans("Intensity"),
// i18n: The symbol/abbreviation for centimeters.
_trans("cm"),
// i18n: Refering to emulated wii remote movement.
_trans("Total travel distance.")},
10 * default_intensity_scale, -50, 50);

// Approximate number of up/down movements in one second.
AddSetting(&m_frequency_setting,
// i18n: Refers to a number of actions per second in Hz.
{_trans("Frequency"),
// i18n: The symbol/abbreviation for hertz (cycles per second).
_trans("Hz"),
// i18n: Refering to emulated wii remote movement.
_trans("Number of shakes per second.")},
6, 1, 20);
}

Shake::StateData Shake::GetState(bool adjusted) const
{
const float x = controls[0]->control_ref->State();
const float y = controls[1]->control_ref->State();
const float z = controls[2]->control_ref->State();

StateData result = {x, y, z};

// FYI: Unadjusted values are used in UI.
if (adjusted)
for (auto& c : result.data)
c = ApplyDeadzone(c, GetDeadzone());

return result;
}

ControlState Shake::GetDeadzone() const
{
return m_deadzone_setting.GetValue() / 100;
}

ControlState Shake::GetIntensity() const
{
return m_intensity_setting.GetValue() / 100;
}

ControlState Shake::GetFrequency() const
{
return m_frequency_setting.GetValue();
}

} // namespace ControllerEmu
@@ -40,4 +40,28 @@ class Force : public ReshapableInput
SettingValue<double> m_jerk_setting;
SettingValue<double> m_angle_setting;
};

class Shake : public ControlGroup
{
public:
using StateData = Common::Vec3;

explicit Shake(const std::string& name, ControlState default_intensity_scale = 1);

StateData GetState(bool adjusted = true) const;

ControlState GetDeadzone() const;

// Return total travel distance in meters.
ControlState GetIntensity() const;

// Return frequency in Hz.
ControlState GetFrequency() const;

private:
SettingValue<double> m_deadzone_setting;
SettingValue<double> m_intensity_setting;
SettingValue<double> m_frequency_setting;
};

} // namespace ControllerEmu
@@ -20,15 +20,15 @@ namespace ControllerEmu
MixedTriggers::MixedTriggers(const std::string& name_)
: ControlGroup(name_, GroupType::MixedTriggers)
{
AddDeadzoneSetting(&m_deadzone_setting, 25);

AddSetting(&m_threshold_setting,
{_trans("Threshold"),
// i18n: The percent symbol.
_trans("%"),
// i18n: Refers to the "threshold" setting for pressure sensitive gamepad inputs.
_trans("Input strength required for activation.")},
90, 0, 100);

AddDeadzoneSetting(&m_deadzone_setting, 25);
}

void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlState* analog,
@@ -46,12 +46,9 @@ void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlSta
const int trigger_count = int(controls.size() / 2);
for (int i = 0; i != trigger_count; ++i)
{
ControlState button_value = controls[i]->control_ref->State();
ControlState analog_value = controls[trigger_count + i]->control_ref->State();

// Apply deadzone:
analog_value = std::max(0.0, analog_value - deadzone) / (1.0 - deadzone);
button_value = std::max(0.0, button_value - deadzone) / (1.0 - deadzone);
const ControlState button_value = ApplyDeadzone(controls[i]->control_ref->State(), deadzone);
ControlState analog_value =
ApplyDeadzone(controls[trigger_count + i]->control_ref->State(), deadzone);

// Apply threshold:
if (button_value > threshold)
@@ -34,9 +34,6 @@ Slider::StateData Slider::GetState()
const ControlState deadzone = m_deadzone_setting.GetValue() / 100;
const ControlState state = controls[1]->control_ref->State() - controls[0]->control_ref->State();

if (fabs(state) > deadzone)
return {(state - (deadzone * sign(state))) / (1 - deadzone)};

return {0.0};
return {ApplyDeadzone(state, deadzone)};
}
} // namespace ControllerEmu
@@ -28,7 +28,7 @@ Triggers::StateData Triggers::GetState()

StateData result(trigger_count);
for (size_t i = 0; i < trigger_count; ++i)
result.data[i] = std::max(controls[i]->control_ref->State() - deadzone, 0.0) / (1 - deadzone);
result.data[i] = ApplyDeadzone(controls[i]->control_ref->State(), deadzone);

return result;
}
@@ -272,8 +272,7 @@ ReshapableInput::ReshapeData ReshapableInput::Reshape(ControlState x, ControlSta
}

// Apply deadzone as a percentage of the user-defined calibration shape/size:
const ControlState deadzone = GetDeadzonePercentage();
dist = std::max(0.0, dist - deadzone) / (1.0 - deadzone);
dist = ApplyDeadzone(dist, GetDeadzonePercentage());

// Scale to the gate shape/radius:
dist *= gate_max_dist;