mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-08 15:20:45 +01:00
Merge pull request #13197 from jordan-woyak/vrr-mapping-indicators
DolphinQt: Update mapping indicators at screen refresh rate.
This commit is contained in:
commit
26ba8f5481
@ -5,7 +5,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@ -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<DT_s>(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());
|
||||
|
@ -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<ControllerEmu::Shake::StateData> m_position_samples;
|
||||
int m_grid_line_position = 0;
|
||||
|
||||
struct ShakeSample
|
||||
{
|
||||
ControllerEmu::Shake::StateData state;
|
||||
float age = 0.f;
|
||||
};
|
||||
|
||||
std::deque<ShakeSample> 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
|
||||
|
@ -27,8 +27,6 @@ class NumericSettingBase;
|
||||
enum class SettingVisibility;
|
||||
} // namespace ControllerEmu
|
||||
|
||||
constexpr int INDICATOR_UPDATE_FREQ = 30;
|
||||
|
||||
class MappingWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -10,12 +10,12 @@
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QScreen>
|
||||
#include <QTabWidget>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#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();
|
||||
|
Loading…
Reference in New Issue
Block a user