diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp index 6a7fbb6ad9..cc38a9382b 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp @@ -5,7 +5,6 @@ #include #include -#include #include @@ -19,12 +18,10 @@ #include "Core/HW/WiimoteEmu/Camera.h" -#include "InputCommon/ControlReference/ControlReference.h" #include "InputCommon/ControllerEmu/Control/Control.h" #include "InputCommon/ControllerEmu/ControlGroup/Cursor.h" #include "InputCommon/ControllerEmu/ControlGroup/Force.h" #include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h" -#include "InputCommon/ControllerEmu/Setting/NumericSetting.h" #include "InputCommon/ControllerInterface/CoreDevice.h" #include "DolphinQt/Config/Mapping/MappingWidget.h" @@ -289,7 +286,14 @@ void GenerateFibonacciSphere(int point_count, F&& callback) void MappingIndicator::paintEvent(QPaintEvent*) { + constexpr float max_elapsed_seconds = 0.1f; + + const auto now = Clock::now(); + const float elapsed_seconds = std::chrono::duration_cast(now - m_last_update).count(); + m_last_update = now; + const auto lock = ControllerEmu::EmulatedController::GetStateLock(); + Update(std::min(elapsed_seconds, max_elapsed_seconds)); Draw(); } @@ -321,7 +325,7 @@ void SquareIndicator::TransformPainter(QPainter& p) p.setRenderHint(QPainter::Antialiasing, true); p.setRenderHint(QPainter::SmoothPixmapTransform, true); - p.translate(width() / 2, height() / 2); + p.translate(width() / 2.0, height() / 2.0); const auto scale = GetContentsScale(); p.scale(scale, scale); } @@ -413,10 +417,13 @@ void AnalogStickIndicator::Draw() (adj_coord.x || adj_coord.y) ? std::make_optional(adj_coord) : std::nullopt); } +void TiltIndicator::Update(float elapsed_seconds) +{ + WiimoteEmu::EmulateTilt(&m_motion_state, &m_group, elapsed_seconds); +} + void TiltIndicator::Draw() { - WiimoteEmu::EmulateTilt(&m_motion_state, &m_group, 1.f / INDICATOR_UPDATE_FREQ); - auto adj_coord = Common::DVec2{-m_motion_state.angle.y, m_motion_state.angle.x} / MathUtil::PI; // Angle values after dividing by pi. @@ -564,28 +571,54 @@ void SwingIndicator::DrawUnderGate(QPainter& p) } } +void SwingIndicator::Update(float elapsed_seconds) +{ + WiimoteEmu::EmulateSwing(&m_motion_state, &m_swing_group, elapsed_seconds); +} + void SwingIndicator::Draw() { - auto& force = m_swing_group; - WiimoteEmu::EmulateSwing(&m_motion_state, &force, 1.f / INDICATOR_UPDATE_FREQ); - - DrawReshapableInput(force, SWING_GATE_COLOR, + DrawReshapableInput(m_swing_group, SWING_GATE_COLOR, Common::DVec2{-m_motion_state.position.x, m_motion_state.position.z}); } +void ShakeMappingIndicator::Update(float elapsed_seconds) +{ + WiimoteEmu::EmulateShake(&m_motion_state, &m_shake_group, elapsed_seconds); + + for (auto& sample : m_position_samples) + sample.age += elapsed_seconds; + + m_position_samples.erase( + std::ranges::find_if(m_position_samples, + [](const ShakeSample& sample) { return sample.age > 1.f; }), + m_position_samples.end()); + + constexpr float MAX_DISTANCE = 0.5f; + + m_position_samples.push_front(ShakeSample{m_motion_state.position / MAX_DISTANCE}); + + const bool any_non_zero_samples = + std::any_of(m_position_samples.begin(), m_position_samples.end(), + [](const ShakeSample& s) { return s.state.LengthSquared() != 0.0; }); + + // Only start moving the line if there's non-zero data. + if (m_grid_line_position || any_non_zero_samples) + { + m_grid_line_position += elapsed_seconds; + + if (m_grid_line_position > 1.f) + { + if (any_non_zero_samples) + m_grid_line_position = std::fmod(m_grid_line_position, 1.f); + else + m_grid_line_position = 0; + } + } +} + void ShakeMappingIndicator::Draw() { - 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(); - QPainter p(this); DrawBoundingBox(p); TransformPainter(p); @@ -610,15 +643,7 @@ void ShakeMappingIndicator::Draw() p.drawPoint(QPointF{-0.5 + c * 0.5, raw_coord.data[c]}); } - // 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; + const double grid_line_x = 1.0 - m_grid_line_position * 2.0; p.setPen(QPen(GetRawInputColor(), 0)); p.drawLine(QPointF{grid_line_x, -1.0}, QPointF{grid_line_x, 1.0}); @@ -629,12 +654,8 @@ void ShakeMappingIndicator::Draw() { QPolygonF polyline; - int i = 0; for (auto& sample : m_position_samples) - { - polyline.append(QPointF{1.0 - i * 2.0 / HISTORY_COUNT, sample.data[c]}); - ++i; - } + polyline.append(QPointF{1.0 - sample.age * 2.0, sample.state.data[c]}); p.setPen(QPen(component_colors[c], 0)); p.drawPolyline(polyline); @@ -692,7 +713,7 @@ void AccelerometerMappingIndicator::Draw() p.setBrush(Qt::NoBrush); p.resetTransform(); - p.translate(width() / 2, height() / 2); + p.translate(width() / 2.0, height() / 2.0); // Red dot upright target. p.setPen(GetAdjustedInputColor()); @@ -717,6 +738,28 @@ void AccelerometerMappingIndicator::Draw() fmt::format("{:.2f} g", state.Length() / WiimoteEmu::GRAVITY_ACCELERATION))); } +void GyroMappingIndicator::Update(float elapsed_seconds) +{ + const auto gyro_state = m_gyro_group.GetState(); + const auto angular_velocity = gyro_state.value_or(Common::Vec3{}); + m_state *= WiimoteEmu::GetRotationFromGyroscope(angular_velocity * Common::Vec3(-1, +1, -1) * + elapsed_seconds); + m_state = m_state.Normalized(); + + // Reset orientation when stable for a bit: + constexpr float STABLE_RESET_SECONDS = 1.f; + // Consider device stable when data (with deadzone applied) is zero. + const bool is_stable = !angular_velocity.LengthSquared(); + + if (!is_stable) + m_stable_time = 0; + else if (m_stable_time < STABLE_RESET_SECONDS) + m_stable_time += elapsed_seconds; + + if (m_stable_time >= STABLE_RESET_SECONDS) + m_state = Common::Quaternion::Identity(); +} + void GyroMappingIndicator::Draw() { const auto gyro_state = m_gyro_group.GetState(); @@ -725,23 +768,9 @@ void GyroMappingIndicator::Draw() const auto jitter = raw_gyro_state - m_previous_velocity; m_previous_velocity = raw_gyro_state; - m_state *= WiimoteEmu::GetRotationFromGyroscope(angular_velocity * Common::Vec3(-1, +1, -1) / - INDICATOR_UPDATE_FREQ); - m_state = m_state.Normalized(); - - // Reset orientation when stable for a bit: - constexpr u32 STABLE_RESET_STEPS = INDICATOR_UPDATE_FREQ; // Consider device stable when data (with deadzone applied) is zero. const bool is_stable = !angular_velocity.LengthSquared(); - if (!is_stable) - m_stable_steps = 0; - else if (m_stable_steps != STABLE_RESET_STEPS) - ++m_stable_steps; - - if (STABLE_RESET_STEPS == m_stable_steps) - m_state = Common::Quaternion::Identity(); - // Use an empty rotation matrix if gyroscope data is not present. const auto rotation = (gyro_state.has_value() ? Common::Matrix33::FromQuaternion(m_state) : Common::Matrix33{}); @@ -814,7 +843,7 @@ void GyroMappingIndicator::Draw() p.setBrush(Qt::NoBrush); p.resetTransform(); - p.translate(width() / 2, height() / 2); + p.translate(width() / 2.0, height() / 2.0); // Red dot upright target. p.setPen(GetAdjustedInputColor()); diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h index 448a86b24a..f571d5072d 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h +++ b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h @@ -45,9 +45,12 @@ public: protected: virtual void Draw() {} + virtual void Update(float elapsed_seconds) {} private: void paintEvent(QPaintEvent*) override; + + Clock::time_point m_last_update = Clock::now(); }; class SquareIndicator : public MappingIndicator @@ -98,6 +101,7 @@ public: private: void Draw() override; + void Update(float elapsed_seconds) override; ControllerEmu::Tilt& m_group; WiimoteEmu::MotionState m_motion_state{}; @@ -132,6 +136,7 @@ public: private: void Draw() override; + void Update(float elapsed_seconds) override; void DrawUnderGate(QPainter& p) override; @@ -146,11 +151,19 @@ public: private: void Draw() override; + void Update(float elapsed_seconds) override; ControllerEmu::Shake& m_shake_group; WiimoteEmu::MotionState m_motion_state{}; - std::deque m_position_samples; - int m_grid_line_position = 0; + + struct ShakeSample + { + ControllerEmu::Shake::StateData state; + float age = 0.f; + }; + + std::deque m_position_samples; + float m_grid_line_position = 0; }; class AccelerometerMappingIndicator : public SquareIndicator @@ -174,11 +187,12 @@ public: private: void Draw() override; + void Update(float elapsed_seconds) override; ControllerEmu::IMUGyroscope& m_gyro_group; Common::Quaternion m_state = Common::Quaternion::Identity(); Common::Vec3 m_previous_velocity = {}; - u32 m_stable_steps = 0; + float m_stable_time = 0; }; class IRPassthroughMappingIndicator : public SquareIndicator diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h index 0ec5227f34..ee05834736 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h @@ -27,8 +27,6 @@ class NumericSettingBase; enum class SettingVisibility; } // namespace ControllerEmu -constexpr int INDICATOR_UPDATE_FREQ = 30; - class MappingWidget : public QWidget { Q_OBJECT diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp index a41920e2c1..d5af344c30 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp @@ -10,12 +10,12 @@ #include #include #include +#include #include #include #include #include -#include "Core/Core.h" #include "Core/HotkeyManager.h" #include "Common/CommonPaths.h" @@ -73,12 +73,15 @@ MappingWindow::MappingWindow(QWidget* parent, Type type, int port_num) SetMappingType(type); const auto timer = new QTimer(this); - connect(timer, &QTimer::timeout, this, [this] { + connect(timer, &QTimer::timeout, this, [this, timer] { + const double refresh_rate = screen()->refreshRate(); + timer->setInterval(1000 / refresh_rate); + const auto lock = GetController()->GetStateLock(); emit Update(); }); - timer->start(1000 / INDICATOR_UPDATE_FREQ); + timer->start(100); const auto lock = GetController()->GetStateLock(); emit ConfigChanged();