fake move: add gyro support

This commit is contained in:
Megamouse 2025-01-08 01:22:50 +01:00
parent 92d0707291
commit 415c2d0795
21 changed files with 458 additions and 187 deletions

View File

@ -1142,7 +1142,7 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
gem_state->handle_pos[3] = 0.f;
// Calculate orientation
if (g_cfg.io.move == move_handler::real)
if (g_cfg.io.move == move_handler::real || (g_cfg.io.move == move_handler::fake && move_data.orientation_enabled))
{
gem_state->quat[0] = move_data.quaternion[0]; // x
gem_state->quat[1] = move_data.quaternion[1]; // y
@ -1151,14 +1151,11 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
}
else
{
static constexpr f32 PI = 3.14159265f;
const auto degree_to_rad = [](f32 degree) -> f32 { return degree * PI / 180.0f; };
const f32 max_angle_per_side_h = g_cfg.io.fake_move_rotation_cone_h / 2.0f;
const f32 max_angle_per_side_v = g_cfg.io.fake_move_rotation_cone_v / 2.0f;
const f32 roll = -degree_to_rad((image_y - half_height) / half_height * max_angle_per_side_v); // This is actually the pitch
const f32 pitch = -degree_to_rad((image_x - half_width) / half_width * max_angle_per_side_h); // This is actually the yaw
const f32 yaw = degree_to_rad(0.0f);
const f32 roll = -PadHandlerBase::degree_to_rad((image_y - half_height) / half_height * max_angle_per_side_v); // This is actually the pitch
const f32 pitch = -PadHandlerBase::degree_to_rad((image_x - half_width) / half_width * max_angle_per_side_h); // This is actually the yaw
const f32 yaw = PadHandlerBase::degree_to_rad(0.0f);
const f32 cr = std::cos(roll * 0.5f);
const f32 sr = std::sin(roll * 0.5f);
const f32 cp = std::cos(pitch * 0.5f);
@ -1318,7 +1315,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& contro
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
{
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y, {});
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y, pad->move_data);
}
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
{
@ -2147,6 +2144,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
switch (g_cfg.io.move)
{
case move_handler::real:
case move_handler::fake:
{
// Get temperature and sensor data
{
@ -2155,7 +2153,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
const auto handler = pad::get_current_handler();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad && pad->m_pad_handler == pad_handler::move && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
inertial_state->temperature = pad->move_data.temperature;
inertial_state->accelerometer[0] = pad->move_data.accelerometer_x;
@ -2170,9 +2168,6 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
break;
}
case move_handler::fake:
ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
break;
case move_handler::mouse:
case move_handler::raw_mouse:
mouse_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);

View File

@ -48,6 +48,7 @@ public:
cfg->pressure_intensity_button.def = "";
cfg->analog_limiter_button.def = "";
cfg->orientation_reset_button.def = "";
// Apply defaults
cfg->from_default();

View File

@ -2,6 +2,7 @@
#include "PadHandler.h"
#include "Emu/system_utils.hpp"
#include "Emu/system_config.h"
#include "Emu/Cell/timers.hpp"
#include "Input/pad_thread.h"
#include "Input/product_info.h"
@ -494,6 +495,12 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr<Pad> pad)
pad->m_analog_limiter_button_index = static_cast<s32>(pad->m_buttons.size()) - 1;
}
if (b_has_orientation)
{
pad->m_buttons.emplace_back(special_button_offset, mapping[button::orientation_reset_button], special_button_value::orientation_reset);
pad->m_orientation_reset_button_index = static_cast<s32>(pad->m_buttons.size()) - 1;
}
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::up], CELL_PAD_CTRL_UP);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::down], CELL_PAD_CTRL_DOWN);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::left], CELL_PAD_CTRL_LEFT);
@ -600,6 +607,11 @@ std::array<std::set<u32>, PadHandlerBase::button::button_count> PadHandlerBase::
mapping[button::analog_limiter_button] = FindKeyCodes<u32, u32>(button_list, cfg->analog_limiter_button);
}
if (b_has_orientation)
{
mapping[button::orientation_reset_button] = FindKeyCodes<u32, u32>(button_list, cfg->orientation_reset_button);
}
return mapping;
}
@ -739,6 +751,8 @@ void PadHandlerBase::process()
if (!device || !pad)
continue;
pad->move_data.orientation_enabled = b_has_orientation && device->config && device->config->orientation_enabled.get();
const connection status = update_connection(device);
switch (status)
@ -754,6 +768,11 @@ void PadHandlerBase::process()
last_connection_status[i] = true;
connected_devices++;
if (b_has_orientation)
{
device->reset_orientation();
}
}
if (status == connection::no_data)
@ -790,6 +809,11 @@ void PadHandlerBase::process()
last_connection_status[i] = false;
connected_devices--;
if (b_has_orientation)
{
device->reset_orientation();
}
}
continue;
}
@ -797,6 +821,142 @@ void PadHandlerBase::process()
get_mapping(m_bindings[i]);
get_extended_info(m_bindings[i]);
get_orientation(m_bindings[i]);
apply_pad_data(m_bindings[i]);
}
}
void PadHandlerBase::set_raw_orientation(ps_move_data& move_data, f32 accel_x, f32 accel_y, f32 accel_z, f32 gyro_x, f32 gyro_y, f32 gyro_z)
{
if (!move_data.orientation_enabled)
{
move_data.reset_sensors();
return;
}
// This function expects DS3 sensor accel values in linear velocity (m/s²) and gyro values in angular velocity (degree/s)
// The default position is flat on the ground, pointing forward.
// The accelerometers constantly measure G forces.
// The gyros measure changes in orientation and will reset when the device isn't moved anymore.
move_data.accelerometer_x = -accel_x; // move_data: Increases if the device is rolled to the left
move_data.accelerometer_y = accel_z; // move_data: Increases if the device is pitched upwards
move_data.accelerometer_z = accel_y; // move_data: Increases if the device is moved upwards
move_data.gyro_x = degree_to_rad(-gyro_x); // move_data: Increases if the device is pitched upwards
move_data.gyro_y = degree_to_rad(gyro_z); // move_data: Increases if the device is rolled to the right
move_data.gyro_z = degree_to_rad(-gyro_y); // move_data: Increases if the device is yawed to the left
}
void PadHandlerBase::set_raw_orientation(Pad& pad)
{
if (!pad.move_data.orientation_enabled)
{
pad.move_data.reset_sensors();
return;
}
// acceleration (linear velocity in m/s²)
const f32 accel_x = (pad.m_sensors[0].m_value - 512) / static_cast<f32>(MOTION_ONE_G);
const f32 accel_y = (pad.m_sensors[1].m_value - 512) / static_cast<f32>(MOTION_ONE_G);
const f32 accel_z = (pad.m_sensors[2].m_value - 512) / static_cast<f32>(MOTION_ONE_G);
// gyro (angular velocity in degree/s)
constexpr f32 gyro_x = 0.0f;
const f32 gyro_y = (pad.m_sensors[3].m_value - 512) / (123.f / 90.f);
constexpr f32 gyro_z = 0.0f;
set_raw_orientation(pad.move_data, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z);
}
void PadHandlerBase::get_orientation(const pad_ensemble& binding) const
{
if (!b_has_orientation) return;
const auto& pad = binding.pad;
const auto& device = binding.device;
if (!pad || !device) return;
if (pad->move_data.calibration_requested)
{
device->reset_orientation();
pad->move_data.quaternion = ps_move_data::default_quaternion;
pad->move_data.calibration_succeeded = true;
return;
}
if (!pad->move_data.orientation_enabled || pad->get_orientation_reset_button_active(pad->m_player_id))
{
// This can be called extensively in quick succession, so let's just reset the pointer instead of creating a new object.
device->ahrs.reset();
pad->move_data.quaternion = ps_move_data::default_quaternion;
return;
}
device->update_orientation(pad->move_data);
}
void PadDevice::reset_orientation()
{
// Initialize Fusion
ahrs = std::make_shared<FusionAhrs>();
FusionAhrsInitialise(ahrs.get());
ahrs->settings.convention = FusionConvention::FusionConventionEnu;
ahrs->settings.gain = 0.0f; // If gain is set, the algorithm tries to adjust the orientation over time.
FusionAhrsSetSettings(ahrs.get(), &ahrs->settings);
FusionAhrsReset(ahrs.get());
}
void PadDevice::update_orientation(ps_move_data& move_data)
{
if (!ahrs)
{
reset_orientation();
}
// Get elapsed time since last update
const u64 now_us = get_system_time();
const float elapsed_sec = (last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - last_ahrs_update_time_us) / 1'000'000.0f);
last_ahrs_update_time_us = now_us;
// The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly.
// Don't ask how the axis work. It's basically been trial and error.
ensure(ahrs->settings.convention == FusionConvention::FusionConventionEnu); // East-North-Up
const FusionVector accelerometer{
.axis {
.x = -move_data.accelerometer_x,
.y = +move_data.accelerometer_y,
.z = +move_data.accelerometer_z
}
};
const FusionVector gyroscope{
.axis {
.x = +PadHandlerBase::rad_to_degree(move_data.gyro_x),
.y = +PadHandlerBase::rad_to_degree(move_data.gyro_z),
.z = -PadHandlerBase::rad_to_degree(move_data.gyro_y)
}
};
FusionVector magnetometer {};
if (move_data.magnetometer_enabled)
{
magnetometer = FusionVector{
.axis {
.x = move_data.magnetometer_x,
.y = move_data.magnetometer_y,
.z = move_data.magnetometer_z
}
};
}
// Update Fusion
FusionAhrsUpdate(ahrs.get(), gyroscope, accelerometer, magnetometer, elapsed_sec);
// Get quaternion
const FusionQuaternion quaternion = FusionAhrsGetQuaternion(ahrs.get());
move_data.quaternion[0] = quaternion.array[1];
move_data.quaternion[1] = quaternion.array[2];
move_data.quaternion[2] = quaternion.array[3];
move_data.quaternion[3] = quaternion.array[0];
}

View File

@ -5,6 +5,15 @@
#include "pad_config_types.h"
#include "util/types.hpp"
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
#include "3rdparty/fusion/fusion/Fusion/Fusion.h"
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
#include <cmath>
#include <functional>
#include <string>
@ -38,6 +47,12 @@ public:
};
color color_override{};
bool color_override_active{};
std::shared_ptr<FusionAhrs> ahrs; // Used to calculate quaternions from sensor data
u64 last_ahrs_update_time_us = 0; // Last ahrs update
void update_orientation(ps_move_data& move_data);
void reset_orientation();
};
struct pad_ensemble
@ -125,6 +140,7 @@ protected:
pressure_intensity_button,
analog_limiter_button,
orientation_reset_button,
button_count
};
@ -153,6 +169,7 @@ protected:
bool b_has_config = false;
bool b_has_pressure_intensity_button = true;
bool b_has_analog_limiter_button = true;
bool b_has_orientation = false;
std::array<cfg_pad, MAX_GAMEPADS> m_pad_configs;
std::vector<pad_ensemble> m_bindings;
@ -301,6 +318,7 @@ public:
bool has_battery_led() const { return b_has_battery_led; }
bool has_pressure_intensity_button() const { return b_has_pressure_intensity_button; }
bool has_analog_limiter_button() const { return b_has_analog_limiter_button; }
bool has_orientation() const { return b_has_orientation; }
u16 NormalizeStickInput(u16 raw_value, s32 threshold, s32 multiplier, bool ignore_threshold = false) const;
void convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 y_in, u32 deadzone, u32 anti_deadzone, u32 padsquircling) const;
@ -323,6 +341,18 @@ public:
virtual void get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array<AnalogSensor, 4>& sensors);
virtual std::unordered_map<u32, std::string> get_motion_axis_list() const { return {}; }
static constexpr f32 PI = 3.14159265f;
static f32 degree_to_rad(f32 degree)
{
return degree * PI / 180.0f;
}
static f32 rad_to_degree(f32 radians)
{
return radians * 180.0f / PI;
};
private:
virtual std::shared_ptr<PadDevice> get_device(const std::string& /*device*/) { return nullptr; }
virtual bool get_is_left_trigger(const std::shared_ptr<PadDevice>& /*device*/, u64 /*keyCode*/) { return false; }
@ -336,10 +366,15 @@ private:
virtual std::unordered_map<u64, u16> get_button_values(const std::shared_ptr<PadDevice>& /*device*/) { return {}; }
virtual pad_preview_values get_preview_values(const std::unordered_map<u64, u16>& /*data*/) { return {}; }
void get_orientation(const pad_ensemble& binding) const;
protected:
virtual std::array<std::set<u32>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg);
virtual void get_mapping(const pad_ensemble& binding);
void TranslateButtonPress(const std::shared_ptr<PadDevice>& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false);
void init_configs();
cfg_pad* get_config(const std::string& pad_id);
static void set_raw_orientation(ps_move_data& move_data, f32 accel_x, f32 accel_y, f32 accel_z, f32 gyro_x, f32 gyro_y, f32 gyro_z);
static void set_raw_orientation(Pad& pad);
};

View File

@ -66,6 +66,9 @@ struct cfg_pad final : cfg::node
cfg_sensor motion_sensor_z{ this, "Motion Sensor Z" };
cfg_sensor motion_sensor_g{ this, "Motion Sensor G" };
cfg::string orientation_reset_button{ this, "Orientation Reset Button", "" };
cfg::_bool orientation_enabled{ this, "Orientation Enabled", false };
cfg::string pressure_intensity_button{ this, "Pressure Intensity Button", "" };
cfg::uint<0, 100> pressure_intensity{ this, "Pressure Intensity Percent", 50 };
cfg::_bool pressure_intensity_toggle_mode{ this, "Pressure Intensity Toggle Mode", false };

View File

@ -159,6 +159,20 @@ u32 get_axis_keycode(u32 offset, u16 value)
}
}
void ps_move_data::reset_sensors()
{
quaternion = default_quaternion;
accelerometer_x = 0.0f;
accelerometer_y = 0.0f;
accelerometer_z = 0.0f;
gyro_x = 0.0f;
gyro_y = 0.0f;
gyro_z = 0.0f;
magnetometer_x = 0.0f;
magnetometer_y = 0.0f;
magnetometer_z = 0.0f;
}
bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_id)
{
if (m_pressure_intensity_button_index < 0)
@ -238,3 +252,13 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id)
return analog_limiter_button.m_pressed;
}
bool Pad::get_orientation_reset_button_active(u32 player_id)
{
if (m_orientation_reset_button_index < 0)
{
return false;
}
return m_buttons[m_orientation_reset_button_index].m_pressed;
}

View File

@ -365,7 +365,8 @@ constexpr u32 special_button_offset = 666; // Must not conflict with other CELL
enum special_button_value
{
pressure_intensity,
analog_limiter
analog_limiter,
orientation_reset
};
struct Button
@ -470,8 +471,10 @@ struct ps_move_data
bool calibration_succeeded = false;
bool magnetometer_enabled = false;
bool orientation_enabled = false;
std::array<f32, 4> quaternion { 1.0f, 0.0f, 0.0f, 0.0f }; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up)
static constexpr std::array<f32, 4> default_quaternion { 1.0f, 0.0f, 0.0f, 0.0f };
std::array<f32, 4> quaternion = default_quaternion; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up)
f32 accelerometer_x = 0.0f; // linear velocity in m/s²
f32 accelerometer_y = 0.0f; // linear velocity in m/s²
f32 accelerometer_z = 0.0f; // linear velocity in m/s²
@ -482,6 +485,8 @@ struct ps_move_data
f32 magnetometer_y = 0.0f;
f32 magnetometer_z = 0.0f;
s16 temperature = 0;
void reset_sensors();
};
struct Pad
@ -512,6 +517,9 @@ struct Pad
bool m_analog_limiter_enabled_last{}; // only used in keyboard_pad_handler
bool get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id);
s32 m_orientation_reset_button_index{-1}; // Special button index. -1 if not set.
bool get_orientation_reset_button_active(u32 player_id);
// Cable State: 0 - 1 plugged in ?
u8 m_cable_state{0};

View File

@ -58,6 +58,7 @@ ds3_pad_handler::ds3_pad_handler()
b_has_rgb = false;
b_has_player_led = true;
b_has_pressure_intensity_button = false; // The DS3 obviously already has this feature natively.
b_has_orientation = true;
m_name_string = "DS3 Pad #";
m_max_devices = CELL_PAD_MAX_PORT_NUM;
@ -199,6 +200,7 @@ void ds3_pad_handler::init_config(cfg_pad* cfg)
cfg->pressure_intensity_button.def = ::at32(button_list, DS3KeyCodes::None);
cfg->analog_limiter_button.def = ::at32(button_list, DS3KeyCodes::None);
cfg->orientation_reset_button.def = ::at32(button_list, DS3KeyCodes::None);
// Set default misc variables
cfg->lstick_anti_deadzone.def = 0;
@ -462,6 +464,9 @@ void ds3_pad_handler::get_extended_info(const pad_ensemble& binding)
//pad->m_sensors[1].m_value = polish_value(pad->m_sensors[1].m_value, 226, 226, 512, 512, 0, 1023);
//pad->m_sensors[2].m_value = polish_value(pad->m_sensors[2].m_value, 113, 113, 512, 512, 0, 1023);
//pad->m_sensors[3].m_value = polish_value(pad->m_sensors[3].m_value, 1, 1, 512, 512, 0, 1023);
// Set raw orientation
set_raw_orientation(*pad);
}
bool ds3_pad_handler::get_is_left_trigger(const std::shared_ptr<PadDevice>& /*device*/, u64 keyCode)

View File

@ -118,6 +118,7 @@ ds4_pad_handler::ds4_pad_handler()
b_has_rgb = true;
b_has_battery = true;
b_has_battery_led = true;
b_has_orientation = true;
m_name_string = "DS4 Pad #";
m_max_devices = CELL_PAD_MAX_PORT_NUM;
@ -179,6 +180,7 @@ void ds4_pad_handler::init_config(cfg_pad* cfg)
cfg->pressure_intensity_button.def = ::at32(button_list, DS4KeyCodes::None);
cfg->analog_limiter_button.def = ::at32(button_list, DS4KeyCodes::None);
cfg->orientation_reset_button.def = ::at32(button_list, DS4KeyCodes::None);
// Set default misc variables
cfg->lstick_anti_deadzone.def = static_cast<u32>(0.13 * thumb_max); // 13%
@ -883,29 +885,27 @@ void ds4_pad_handler::get_extended_info(const pad_ensemble& binding)
// these values come already calibrated, all we need to do is convert to ds3 range
// accel
f32 accelX = static_cast<s16>(input.accel[0]) / static_cast<f32>(DS4_ACC_RES_PER_G) * -1;
f32 accelY = static_cast<s16>(input.accel[1]) / static_cast<f32>(DS4_ACC_RES_PER_G) * -1;
f32 accelZ = static_cast<s16>(input.accel[2]) / static_cast<f32>(DS4_ACC_RES_PER_G) * -1;
// acceleration (linear velocity in m/s²)
const f32 accel_x = static_cast<s16>(input.accel[0]) / static_cast<f32>(DS4_ACC_RES_PER_G) * -1;
const f32 accel_y = static_cast<s16>(input.accel[1]) / static_cast<f32>(DS4_ACC_RES_PER_G) * -1;
const f32 accel_z = static_cast<s16>(input.accel[2]) / static_cast<f32>(DS4_ACC_RES_PER_G) * -1;
// gyro (angular velocity in degree/s)
const f32 gyro_x = static_cast<s16>(input.gyro[0]) / static_cast<f32>(DS4_GYRO_RES_PER_DEG_S) * -1;
const f32 gyro_y = static_cast<s16>(input.gyro[1]) / static_cast<f32>(DS4_GYRO_RES_PER_DEG_S) * -1;
const f32 gyro_z = static_cast<s16>(input.gyro[2]) / static_cast<f32>(DS4_GYRO_RES_PER_DEG_S) * -1;
// now just use formula from ds3
accelX = accelX * 113 + 512;
accelY = accelY * 113 + 512;
accelZ = accelZ * 113 + 512;
pad->m_sensors[0].m_value = Clamp0To1023(accelX);
pad->m_sensors[1].m_value = Clamp0To1023(accelY);
pad->m_sensors[2].m_value = Clamp0To1023(accelZ);
// gyroY is yaw, which is all that we need
//f32 gyroX = static_cast<s16>(input.gyro[0]) / static_cast<f32>(DS4_GYRO_RES_PER_DEG_S) * -1;
f32 gyroY = static_cast<s16>(input.gyro[1]) / static_cast<f32>(DS4_GYRO_RES_PER_DEG_S) * -1;
//f32 gyroZ = static_cast<s16>(input.gyro[2]) / static_cast<f32>(DS4_GYRO_RES_PER_DEG_S) * -1;
pad->m_sensors[0].m_value = Clamp0To1023(accel_x * MOTION_ONE_G + 512);
pad->m_sensors[1].m_value = Clamp0To1023(accel_y * MOTION_ONE_G + 512);
pad->m_sensors[2].m_value = Clamp0To1023(accel_z * MOTION_ONE_G + 512);
// gyro_y is yaw, which is all that we need.
// Convert to ds3. The ds3 resolution is 123/90°/sec.
gyroY = gyroY * (123.f / 90.f) + 512;
pad->m_sensors[3].m_value = Clamp0To1023(gyro_y * (123.f / 90.f) + 512);
pad->m_sensors[3].m_value = Clamp0To1023(gyroY);
// Set raw orientation
set_raw_orientation(pad->move_data, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z);
}
void ds4_pad_handler::apply_pad_data(const pad_ensemble& binding)

View File

@ -90,6 +90,7 @@ dualsense_pad_handler::dualsense_pad_handler()
b_has_player_led = true;
b_has_battery = true;
b_has_battery_led = true;
b_has_orientation = true;
m_name_string = "DualSense Pad #";
m_max_devices = CELL_PAD_MAX_PORT_NUM;
@ -252,6 +253,7 @@ void dualsense_pad_handler::init_config(cfg_pad* cfg)
cfg->pressure_intensity_button.def = ::at32(button_list, DualSenseKeyCodes::None);
cfg->analog_limiter_button.def = ::at32(button_list, DualSenseKeyCodes::None);
cfg->orientation_reset_button.def = ::at32(button_list, DualSenseKeyCodes::None);
// Set default misc variables
cfg->lstick_anti_deadzone.def = static_cast<u32>(0.13 * thumb_max); // 13%
@ -614,28 +616,27 @@ void dualsense_pad_handler::get_extended_info(const pad_ensemble& binding)
// these values come already calibrated, all we need to do is convert to ds3 range
// gyroY is yaw, which is all that we need
//f32 gyroX = static_cast<s16>(input.gyro[0]) / static_cast<f32>(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f;
f32 gyroY = static_cast<s16>(input.gyro[1]) / static_cast<f32>(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f;
//f32 gyroZ = static_cast<s16>(input.gyro[2]) / static_cast<f32>(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f;
// gyro (angular velocity in degree/s)
const f32 gyro_x = static_cast<s16>(input.gyro[0]) / static_cast<f32>(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f;
const f32 gyro_y = static_cast<s16>(input.gyro[1]) / static_cast<f32>(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f;
const f32 gyro_z = static_cast<s16>(input.gyro[2]) / static_cast<f32>(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f;
// accel
f32 accelX = static_cast<s16>(input.accel[0]) / static_cast<f32>(DUALSENSE_ACC_RES_PER_G) * -1;
f32 accelY = static_cast<s16>(input.accel[1]) / static_cast<f32>(DUALSENSE_ACC_RES_PER_G) * -1;
f32 accelZ = static_cast<s16>(input.accel[2]) / static_cast<f32>(DUALSENSE_ACC_RES_PER_G) * -1;
// acceleration (linear velocity in m/s²)
const f32 accel_x = static_cast<s16>(input.accel[0]) / static_cast<f32>(DUALSENSE_ACC_RES_PER_G) * -1;
const f32 accel_y = static_cast<s16>(input.accel[1]) / static_cast<f32>(DUALSENSE_ACC_RES_PER_G) * -1;
const f32 accel_z = static_cast<s16>(input.accel[2]) / static_cast<f32>(DUALSENSE_ACC_RES_PER_G) * -1;
// now just use formula from ds3
accelX = accelX * 113 + 512;
accelY = accelY * 113 + 512;
accelZ = accelZ * 113 + 512;
pad->m_sensors[0].m_value = Clamp0To1023(accel_x * MOTION_ONE_G + 512);
pad->m_sensors[1].m_value = Clamp0To1023(accel_y * MOTION_ONE_G + 512);
pad->m_sensors[2].m_value = Clamp0To1023(accel_z * MOTION_ONE_G + 512);
// gyro_y is yaw, which is all that we need
// Convert to ds3. The ds3 resolution is 123/90°/sec.
gyroY = gyroY * (123.f / 90.f) + 512;
pad->m_sensors[3].m_value = Clamp0To1023(gyro_y * (123.f / 90.f) + 512);
pad->m_sensors[0].m_value = Clamp0To1023(accelX);
pad->m_sensors[1].m_value = Clamp0To1023(accelY);
pad->m_sensors[2].m_value = Clamp0To1023(accelZ);
pad->m_sensors[3].m_value = Clamp0To1023(gyroY);
// Set raw orientation
set_raw_orientation(pad->move_data, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z);
}
std::unordered_map<u64, u16> dualsense_pad_handler::get_button_values(const std::shared_ptr<PadDevice>& device)

View File

@ -34,6 +34,7 @@ evdev_joystick_handler::evdev_joystick_handler()
b_has_rumble = true;
b_has_motion = true;
b_has_deadzones = true;
b_has_orientation = true;
m_trigger_threshold = trigger_max / 2;
m_thumb_threshold = thumb_max / 2;
@ -84,6 +85,7 @@ void evdev_joystick_handler::init_config(cfg_pad* cfg)
cfg->pressure_intensity_button.def = ::at32(button_list, NO_BUTTON);
cfg->analog_limiter_button.def = ::at32(button_list, NO_BUTTON);
cfg->orientation_reset_button.def = ::at32(button_list, NO_BUTTON);
// Set default misc variables
cfg->lstick_anti_deadzone.def = static_cast<u32>(0.13 * thumb_max); // 13%
@ -1075,6 +1077,8 @@ void evdev_joystick_handler::get_extended_info(const pad_ensemble& binding)
}
}
set_raw_orientation(*pad);
if (ret < 0)
{
// -EAGAIN signifies no available events, not an actual *error*.
@ -1382,6 +1386,12 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr<Pad> pad)
pad->m_analog_limiter_button_index = static_cast<s32>(pad->m_buttons.size()) - 1;
}
if (b_has_orientation)
{
pad->m_buttons.emplace_back(special_button_offset, find_buttons(cfg->orientation_reset_button), special_button_value::orientation_reset);
pad->m_orientation_reset_button_index = static_cast<s32>(pad->m_buttons.size()) - 1;
}
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->triangle), CELL_PAD_CTRL_TRIANGLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->circle), CELL_PAD_CTRL_CIRCLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->cross), CELL_PAD_CTRL_CROSS);

View File

@ -127,6 +127,7 @@ ps_move_handler::ps_move_handler()
b_has_battery = true;
b_has_battery_led = false;
b_has_pressure_intensity_button = false;
b_has_orientation = true;
m_name_string = "PS Move #";
m_max_devices = 4; // CELL_GEM_MAX_NUM
@ -170,6 +171,8 @@ void ps_move_handler::init_config(cfg_pad* cfg)
cfg->l2.def = ::at32(button_list, ps_move_key_codes::none);
cfg->l3.def = ::at32(button_list, ps_move_key_codes::none);
cfg->orientation_reset_button.def = ::at32(button_list, ps_move_key_codes::none);
// Set default misc variables
cfg->lstickdeadzone.def = 40; // between 0 and 255
cfg->rstickdeadzone.def = 40; // between 0 and 255
@ -359,8 +362,6 @@ void ps_move_handler::check_add_device(hid_device* hidDevice, std::string_view p
psmove_parse_calibration(calibration, *device);
}
device->reset_orientation();
// Activate
if (send_output_report(device) == -1)
{
@ -669,12 +670,12 @@ void ps_move_handler::get_extended_info(const pad_ensemble& binding)
// The default position is flat on the ground, pointing forward.
// The accelerometers constantly measure G forces.
// The gyros measure changes in orientation and will reset when the device isn't moved anymore.
s16 accel_x = input.accel_x_1; // Increases if the device is rolled to the left
s16 accel_y = input.accel_y_1; // Increases if the device is pitched upwards
s16 accel_z = input.accel_z_1; // Increases if the device is moved upwards
s16 gyro_x = input.gyro_x_1; // Increases if the device is pitched upwards
s16 gyro_y = input.gyro_y_1; // Increases if the device is rolled to the right
s16 gyro_z = input.gyro_z_1; // Increases if the device is yawed to the left
f32 accel_x = input.accel_x_1; // Increases if the device is rolled to the left
f32 accel_y = input.accel_y_1; // Increases if the device is pitched upwards
f32 accel_z = input.accel_z_1; // Increases if the device is moved upwards
f32 gyro_x = input.gyro_x_1; // Increases if the device is pitched upwards
f32 gyro_y = input.gyro_y_1; // Increases if the device is rolled to the right
f32 gyro_z = input.gyro_z_1; // Increases if the device is yawed to the left
if (dev->model == ps_move_model::ZCM1)
{
@ -684,45 +685,60 @@ void ps_move_handler::get_extended_info(const pad_ensemble& binding)
gyro_x -= zero_shift;
gyro_y -= zero_shift;
gyro_z -= zero_shift;
const ps_move_input_report_ZCM1& input_zcm1 = dev->input_report_ZCM1;
#define TWELVE_BIT_SIGNED(x) (((x) & 0x800) ? (-(((~(x)) & 0xFFF) + 1)) : (x))
pad->move_data.magnetometer_x = static_cast<f32>(TWELVE_BIT_SIGNED(((input.magnetometer_x & 0x0F) << 8) | input_zcm1.magnetometer_x2));
pad->move_data.magnetometer_y = static_cast<f32>(TWELVE_BIT_SIGNED((input_zcm1.magnetometer_y << 4) | (input_zcm1.magnetometer_yz & 0xF0) >> 4));
pad->move_data.magnetometer_z = static_cast<f32>(TWELVE_BIT_SIGNED(((input_zcm1.magnetometer_yz & 0x0F) << 8) | input_zcm1.magnetometer_z));
}
// Apply calibration
if (dev->calibration.is_valid)
if (!device->config || !device->config->orientation_enabled)
{
pad->move_data.accelerometer_x = accel_x * dev->calibration.accel_x_factor + dev->calibration.accel_x_offset;
pad->move_data.accelerometer_y = accel_y * dev->calibration.accel_y_factor + dev->calibration.accel_y_offset;
pad->move_data.accelerometer_z = accel_z * dev->calibration.accel_z_factor + dev->calibration.accel_z_offset;
pad->move_data.gyro_x = (gyro_x - dev->calibration.gyro_x_offset) * dev->calibration.gyro_x_gain;
pad->move_data.gyro_y = (gyro_y - dev->calibration.gyro_y_offset) * dev->calibration.gyro_y_gain;
pad->move_data.gyro_z = (gyro_z - dev->calibration.gyro_z_offset) * dev->calibration.gyro_z_gain;
pad->move_data.reset_sensors();
}
else
{
constexpr f32 MOVE_ONE_G = 4096.0f; // This is just a rough estimate and probably depends on the device
// Apply calibration
if (dev->calibration.is_valid)
{
accel_x = accel_x * dev->calibration.accel_x_factor + dev->calibration.accel_x_offset;
accel_y = accel_y * dev->calibration.accel_y_factor + dev->calibration.accel_y_offset;
accel_z = accel_z * dev->calibration.accel_z_factor + dev->calibration.accel_z_offset;
gyro_x = (gyro_x - dev->calibration.gyro_x_offset) * dev->calibration.gyro_x_gain;
gyro_y = (gyro_y - dev->calibration.gyro_y_offset) * dev->calibration.gyro_y_gain;
gyro_z = (gyro_z - dev->calibration.gyro_z_offset) * dev->calibration.gyro_z_gain;
}
else
{
constexpr f32 MOVE_ONE_G = 4096.0f; // This is just a rough estimate and probably depends on the device
pad->move_data.accelerometer_x = accel_x / MOVE_ONE_G;
pad->move_data.accelerometer_y = accel_y / MOVE_ONE_G;
pad->move_data.accelerometer_z = accel_z / MOVE_ONE_G;
pad->move_data.gyro_x = gyro_x / MOVE_ONE_G;
pad->move_data.gyro_y = gyro_y / MOVE_ONE_G;
pad->move_data.gyro_z = gyro_z / MOVE_ONE_G;
accel_x /= MOVE_ONE_G;
accel_y /= MOVE_ONE_G;
accel_z /= MOVE_ONE_G;
gyro_x /= MOVE_ONE_G;
gyro_y /= MOVE_ONE_G;
gyro_z /= MOVE_ONE_G;
}
pad->move_data.accelerometer_x = accel_x;
pad->move_data.accelerometer_y = accel_y;
pad->move_data.accelerometer_z = accel_z;
pad->move_data.gyro_x = gyro_x;
pad->move_data.gyro_y = gyro_y;
pad->move_data.gyro_z = gyro_z;
if (dev->model == ps_move_model::ZCM1)
{
const ps_move_input_report_ZCM1& input_zcm1 = dev->input_report_ZCM1;
#define TWELVE_BIT_SIGNED(x) (((x) & 0x800) ? (-(((~(x)) & 0xFFF) + 1)) : (x))
pad->move_data.magnetometer_x = static_cast<f32>(TWELVE_BIT_SIGNED(((input.magnetometer_x & 0x0F) << 8) | input_zcm1.magnetometer_x2));
pad->move_data.magnetometer_y = static_cast<f32>(TWELVE_BIT_SIGNED((input_zcm1.magnetometer_y << 4) | (input_zcm1.magnetometer_yz & 0xF0) >> 4));
pad->move_data.magnetometer_z = static_cast<f32>(TWELVE_BIT_SIGNED(((input_zcm1.magnetometer_yz & 0x0F) << 8) | input_zcm1.magnetometer_z));
}
}
pad->move_data.temperature = ((input.temperature << 4) | ((input.magnetometer_x & 0xF0) >> 4));
pad->m_sensors[0].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_x * -1.0f));
pad->m_sensors[1].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_y * -1.0f));
pad->m_sensors[2].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_z));
pad->m_sensors[3].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.gyro_z * -1.0f));
dev->update_orientation(pad->move_data);
pad->m_sensors[0].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * accel_x * -1.0f));
pad->m_sensors[1].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * accel_y * -1.0f));
pad->m_sensors[2].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * accel_z));
pad->m_sensors[3].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * gyro_z * -1.0f));
handle_external_device(binding);
}
@ -863,74 +879,3 @@ u32 ps_move_handler::get_battery_level(const std::string& padId)
// 0 to 5
return std::clamp<u32>(device->battery_level * 20, 0, 100);
}
void ps_move_device::reset_orientation()
{
// Initialize Fusion
ahrs = {};
FusionAhrsInitialise(&ahrs);
ahrs.settings.convention = FusionConvention::FusionConventionEnu;
ahrs.settings.gain = 0.0f; // If gain is set, the algorithm tries to adjust the orientation over time.
FusionAhrsSetSettings(&ahrs, &ahrs.settings);
FusionAhrsReset(&ahrs);
}
void ps_move_device::update_orientation(ps_move_data& move_data)
{
if (move_data.calibration_requested)
{
reset_orientation();
move_data.calibration_succeeded = true;
}
// Get elapsed time since last update
const u64 now_us = get_system_time();
const float elapsed_sec = (last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - last_ahrs_update_time_us) / 1'000'000.0f);
last_ahrs_update_time_us = now_us;
// The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly.
// Don't ask how the axis work. It's basically been trial and error.
ensure(ahrs.settings.convention == FusionConvention::FusionConventionEnu); // East-North-Up
const FusionVector accelerometer{
.axis {
.x = -move_data.accelerometer_x,
.y = +move_data.accelerometer_y,
.z = +move_data.accelerometer_z
}
};
static constexpr f32 PI = 3.14159265f;
const auto rad_to_degree = [](f32 radians) -> f32 { return radians * 180.0f / PI; };
const FusionVector gyroscope{
.axis {
.x = +rad_to_degree(move_data.gyro_x),
.y = +rad_to_degree(move_data.gyro_z),
.z = -rad_to_degree(move_data.gyro_y)
}
};
FusionVector magnetometer {};
if (move_data.magnetometer_enabled)
{
magnetometer = FusionVector{
.axis {
.x = move_data.magnetometer_x,
.y = move_data.magnetometer_y,
.z = move_data.magnetometer_z
}
};
}
// Update Fusion
FusionAhrsUpdate(&ahrs, gyroscope, accelerometer, magnetometer, elapsed_sec);
// Get quaternion
const FusionQuaternion quaternion = FusionAhrsGetQuaternion(&ahrs);
move_data.quaternion[0] = quaternion.array[1];
move_data.quaternion[1] = quaternion.array[2];
move_data.quaternion[2] = quaternion.array[3];
move_data.quaternion[3] = quaternion.array[0];
}

View File

@ -146,12 +146,6 @@ public:
u32 external_device_id = 0;
ps_move_calibration calibration{};
FusionAhrs ahrs {}; // Used to calculate quaternions from sensor data
u64 last_ahrs_update_time_us = 0; // Last ahrs update
void update_orientation(ps_move_data& move_data);
void reset_orientation();
const reports::ps_move_input_report_common& input_report_common() const;
};

View File

@ -179,6 +179,7 @@ sdl_pad_handler::sdl_pad_handler() : PadHandlerBase(pad_handler::sdl)
b_has_rgb = true;
b_has_battery = true;
b_has_battery_led = true;
b_has_orientation = true;
m_trigger_threshold = trigger_max / 2;
m_thumb_threshold = thumb_max / 2;
@ -233,6 +234,7 @@ void sdl_pad_handler::init_config(cfg_pad* cfg)
cfg->pressure_intensity_button.def = ::at32(button_list, SDLKeyCodes::None);
cfg->analog_limiter_button.def = ::at32(button_list, SDLKeyCodes::None);
cfg->orientation_reset_button.def = ::at32(button_list, SDLKeyCodes::None);
// Set default misc variables
cfg->lstick_anti_deadzone.def = static_cast<u32>(0.13 * thumb_max); // 13%
@ -732,14 +734,14 @@ void sdl_pad_handler::get_extended_info(const pad_ensemble& binding)
}
else
{
const float& accel_x = dev->values_accel[0]; // Angular speed around the x axis (pitch)
const float& accel_y = dev->values_accel[1]; // Angular speed around the y axis (yaw)
const float& accel_z = dev->values_accel[2]; // Angular speed around the z axis (roll
const f32 accel_x = dev->values_accel[0]; // Angular speed around the x axis (pitch)
const f32 accel_y = dev->values_accel[1]; // Angular speed around the y axis (yaw)
const f32 accel_z = dev->values_accel[2]; // Angular speed around the z axis (roll
// Convert to ds3. The ds3 resolution is 113/G.
pad->m_sensors[0].m_value = Clamp0To1023((accel_x / SDL_STANDARD_GRAVITY) * -1 * 113 + 512);
pad->m_sensors[1].m_value = Clamp0To1023((accel_y / SDL_STANDARD_GRAVITY) * -1 * 113 + 512);
pad->m_sensors[2].m_value = Clamp0To1023((accel_z / SDL_STANDARD_GRAVITY) * -1 * 113 + 512);
pad->m_sensors[0].m_value = Clamp0To1023((accel_x / SDL_STANDARD_GRAVITY) * -1 * MOTION_ONE_G + 512);
pad->m_sensors[1].m_value = Clamp0To1023((accel_y / SDL_STANDARD_GRAVITY) * -1 * MOTION_ONE_G + 512);
pad->m_sensors[2].m_value = Clamp0To1023((accel_z / SDL_STANDARD_GRAVITY) * -1 * MOTION_ONE_G + 512);
}
}
@ -751,16 +753,20 @@ void sdl_pad_handler::get_extended_info(const pad_ensemble& binding)
}
else
{
//const float& gyro_x = dev->values_gyro[0]; // Angular speed around the x axis (pitch)
const float& gyro_y = dev->values_gyro[1]; // Angular speed around the y axis (yaw)
//const float& gyro_z = dev->values_gyro[2]; // Angular speed around the z axis (roll)
//const f32 gyro_x = dev->values_gyro[0]; // Angular speed around the x axis (pitch)
const f32 gyro_y = dev->values_gyro[1]; // Angular speed around the y axis (yaw)
//const f32 gyro_z = dev->values_gyro[2]; // Angular speed around the z axis (roll)
// Convert to ds3. The ds3 resolution is 123/90°/sec. The SDL gyro is measured in rad/sec.
static constexpr f32 PI = 3.14159265f;
const float degree = (gyro_y * 180.0f / PI);
const f32 degree = rad_to_degree(gyro_y);
pad->m_sensors[3].m_value = Clamp0To1023(degree * (123.f / 90.f) + 512);
}
}
if (dev->sdl.has_accel || dev->sdl.has_gyro)
{
set_raw_orientation(*pad);
}
}
void sdl_pad_handler::get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array<AnalogSensor, 4>& sensors)

View File

@ -46,8 +46,8 @@ public:
bool has_accel = false;
bool has_gyro = false;
float data_rate_accel = 0.0f;
float data_rate_gyro = 0.0f;
f32 data_rate_accel = 0.0f;
f32 data_rate_gyro = 0.0f;
std::set<SDL_GameControllerButton> button_ids;
std::set<SDL_GameControllerAxis> axis_ids;
@ -57,8 +57,8 @@ public:
sdl_info sdl{};
std::array<float, 3> values_accel{};
std::array<float, 3> values_gyro{};
std::array<f32, 3> values_accel{};
std::array<f32, 3> values_gyro{};
bool led_needs_update = true;
bool led_is_on = true;

View File

@ -84,6 +84,7 @@ skateboard_pad_handler::skateboard_pad_handler()
b_has_battery = false;
b_has_battery_led = false;
b_has_pressure_intensity_button = false;
b_has_orientation = true;
m_name_string = "Skateboard #";
m_max_devices = CELL_PAD_MAX_PORT_NUM;
@ -134,6 +135,8 @@ void skateboard_pad_handler::init_config(cfg_pad* cfg)
cfg->tilt_left.def = ::at32(button_list, skateboard_key_codes::tilt_left);
cfg->tilt_right.def = ::at32(button_list, skateboard_key_codes::tilt_right);
cfg->orientation_reset_button.def = ::at32(button_list, skateboard_key_codes::none);
// Set default misc variables
cfg->lstick_anti_deadzone.def = 0;
cfg->rstick_anti_deadzone.def = 0;
@ -323,6 +326,8 @@ void skateboard_pad_handler::get_extended_info(const pad_ensemble& binding)
pad->m_sensors[1].m_value = Clamp0To1023(input.large_axes[1]);
pad->m_sensors[2].m_value = Clamp0To1023(input.large_axes[2]);
pad->m_sensors[3].m_value = Clamp0To1023(input.large_axes[3]);
set_raw_orientation(*pad);
}
pad_preview_values skateboard_pad_handler::get_preview_values(const std::unordered_map<u64, u16>& /*data*/)

View File

@ -64,6 +64,7 @@ xinput_pad_handler::xinput_pad_handler() : PadHandlerBase(pad_handler::xinput)
b_has_deadzones = true;
b_has_battery = true;
b_has_battery_led = false;
b_has_orientation = false;
m_name_string = "XInput Pad #";
m_max_devices = XUSER_MAX_COUNT;
@ -119,6 +120,7 @@ void xinput_pad_handler::init_config(cfg_pad* cfg)
cfg->pressure_intensity_button.def = ::at32(button_list, XInputKeyCodes::None);
cfg->analog_limiter_button.def = ::at32(button_list, XInputKeyCodes::None);
cfg->orientation_reset_button.def = ::at32(button_list, XInputKeyCodes::None);
// Set default misc variables
cfg->lstick_anti_deadzone.def = static_cast<u32>(0.13 * thumb_max); // 13%
@ -413,6 +415,8 @@ bool xinput_pad_handler::Init()
}
}
b_has_orientation = !!xinputGetCustomData;
if (!m_is_init)
return false;
@ -551,6 +555,8 @@ void xinput_pad_handler::get_extended_info(const pad_ensemble& binding)
pad->m_sensors[1].m_value = sensors.SCP_ACCEL_Y;
pad->m_sensors[2].m_value = sensors.SCP_ACCEL_Z;
pad->m_sensors[3].m_value = sensors.SCP_GYRO;
set_raw_orientation(*pad);
}
}
}

View File

@ -312,6 +312,7 @@ void pad_settings_dialog::InitButtons()
insert_button(button_ids::id_pressure_intensity, ui->b_pressure_intensity);
insert_button(button_ids::id_analog_limiter, ui->b_analog_limiter);
insert_button(button_ids::id_orientation_reset, ui->b_orientation_reset);
m_pad_buttons->addButton(ui->b_refresh, button_ids::id_refresh);
m_pad_buttons->addButton(ui->b_addConfig, button_ids::id_add_config_file);
@ -720,6 +721,7 @@ void pad_settings_dialog::ReloadButtons()
updateButton(button_ids::id_pressure_intensity, ui->b_pressure_intensity, &cfg.pressure_intensity_button);
updateButton(button_ids::id_analog_limiter, ui->b_analog_limiter, &cfg.analog_limiter_button);
updateButton(button_ids::id_orientation_reset, ui->b_orientation_reset, &cfg.orientation_reset_button);
UpdateLabels(true);
}
@ -1195,6 +1197,9 @@ void pad_settings_dialog::UpdateLabels(bool is_reset)
RepaintPreviewLabel(ui->preview_stick_left, ui->slider_stick_left->value(), ui->anti_deadzone_slider_stick_left->value(), ui->slider_stick_left->size().width(), m_lx, m_ly, cfg.lpadsquircling, cfg.lstickmultiplier / 100.0);
RepaintPreviewLabel(ui->preview_stick_right, ui->slider_stick_right->value(), ui->anti_deadzone_slider_stick_right->value(), ui->slider_stick_right->size().width(), m_rx, m_ry, cfg.rpadsquircling, cfg.rstickmultiplier / 100.0);
// Update orientation toggle
ui->cb_orientation_toggle->setChecked(cfg.orientation_enabled.get());
// Update analog limiter toggle mode
ui->cb_analog_limiter_toggle_mode->setChecked(cfg.analog_limiter_toggle_mode.get());
@ -1249,6 +1254,7 @@ void pad_settings_dialog::SwitchButtons(bool is_enabled)
ui->gb_pressure_intensity_deadzone->setEnabled(is_enabled);
ui->gb_pressure_intensity->setEnabled(is_enabled && m_enable_pressure_intensity_button);
ui->gb_analog_limiter->setEnabled(is_enabled && m_enable_analog_limiter_button);
ui->gb_orientation_reset->setEnabled(is_enabled && m_enable_orientation_reset_button);
ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble);
ui->gb_motion_controls->setEnabled(is_enabled && m_enable_motion);
ui->gb_stick_deadzones->setEnabled(is_enabled && m_enable_deadzones);
@ -1467,11 +1473,15 @@ void pad_settings_dialog::ChangeHandler()
// Enable Analog Limiter Settings
m_enable_analog_limiter_button = m_handler->has_analog_limiter_button();
// Enable Orientation Reset Settings
m_enable_orientation_reset_button = m_handler->has_orientation();
// Change our contextual widgets
ui->left_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0);
ui->right_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0);
ui->gb_pressure_intensity->setVisible(m_handler->has_pressure_intensity_button());
ui->gb_analog_limiter->setVisible(m_handler->has_analog_limiter_button());
ui->gb_orientation_reset->setVisible(m_handler->has_orientation());
// Update device dropdown and block signals while doing so
ui->chooseDevice->blockSignals(true);
@ -1829,8 +1839,11 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id)
for (const auto& [id, button] : m_cfg_entries)
{
// Let's ignore special keys, unless we're using a keyboard
if ((id == button_ids::id_pressure_intensity || id == button_ids::id_analog_limiter) && m_handler->m_type != pad_handler::keyboard)
if (m_handler->m_type != pad_handler::keyboard &&
(id == button_ids::id_pressure_intensity || id == button_ids::id_analog_limiter || id == button_ids::id_orientation_reset))
{
continue;
}
for (const std::string& key : cfg_pad::get_buttons(button.keys))
{
@ -1886,6 +1899,11 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id)
cfg.pressure_intensity_toggle_mode.set(ui->cb_pressure_intensity_toggle_mode->isChecked());
}
if (m_handler->has_orientation())
{
cfg.orientation_enabled.set(ui->cb_orientation_toggle->isChecked());
}
cfg.pressure_intensity_deadzone.set(ui->pressure_intensity_deadzone->value());
if (m_handler->m_type == pad_handler::keyboard)
@ -2077,6 +2095,7 @@ void pad_settings_dialog::SubscribeTooltips()
// Localized tooltips
const Tooltips tooltips;
SubscribeTooltip(ui->gb_orientation_reset, tooltips.gamepad_settings.orientation_reset);
SubscribeTooltip(ui->gb_analog_limiter, tooltips.gamepad_settings.analog_limiter);
SubscribeTooltip(ui->gb_pressure_intensity, tooltips.gamepad_settings.pressure_intensity);
SubscribeTooltip(ui->gb_pressure_intensity_deadzone, tooltips.gamepad_settings.pressure_deadzone);

View File

@ -66,6 +66,7 @@ class pad_settings_dialog : public QDialog
id_pressure_intensity, // Special button for pressure intensity
id_analog_limiter, // Special button for analog limiter
id_orientation_reset, // Special button for orientation reset
id_pad_end, // end
@ -123,6 +124,7 @@ private:
bool m_enable_motion{ false };
bool m_enable_pressure_intensity_button{ true };
bool m_enable_analog_limiter_button{ true };
bool m_enable_orientation_reset_button{ true };
// Button Mapping
QButtonGroup* m_pad_buttons = nullptr;

View File

@ -36,9 +36,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-71</y>
<width>1290</width>
<height>907</height>
<y>0</y>
<width>1292</width>
<height>902</height>
</rect>
</property>
<widget class="QWidget" name="tab">
@ -669,6 +669,57 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_orientation_reset">
<property name="title">
<string>Orientation Reset</string>
</property>
<layout class="QHBoxLayout" name="orientation_reset_layout" stretch="2,1,1">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="b_orientation_reset">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item>
<spacer name="spacer_orientation_reset">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="cb_orientation_toggle">
<property name="text">
<string>Orientation</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_analog_limiter">
<property name="title">
@ -695,12 +746,12 @@
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<spacer name="spacer_analog_limiter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -2286,7 +2337,7 @@
<item>
<spacer name="spacer_squircle_left">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -2316,7 +2367,7 @@
<item>
<spacer name="spacer_squircle_right">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -2364,7 +2415,7 @@
<item>
<spacer name="spacer_stick_multi_left">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -2391,7 +2442,7 @@
<item>
<spacer name="spacer_stick_multi_right">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

View File

@ -287,6 +287,7 @@ public:
const QString mmjoy = tr("The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them.");
const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms.");
const QString orientation_reset = tr("Resets the sensor orientation when pressed.<br>Toggle the checkbox to enable or disable the orientation feature.<br>Currently only used for PS Move interactions.");
const QString analog_limiter = tr("Applies the stick multipliers while this special button is pressed.<br>Enable \"Toggle\" if you want to toggle the analog limiter on button press instead.");
const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.<br>Enable \"Toggle\" if you want to toggle the intensity on button press instead.<br>Use the percentage to change how hard you want to press a button.");
const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range.");