Look Mode For Motion Emulation (#2422)

* Added a More Precise "Look Mode" to Emulated Gyro

* Fix some issues and locking

---------

Co-authored-by: PabloMK7 <hackyglitch2@gmail.com>
This commit is contained in:
SpinCodeItandPabloMK7 authored and GitHub committed 2026-11-05 22:25:45 +01:00
1 parent e75e4c63a1
commit 17ada5ec8b
4 files changed
+104 -14

No files matched your search

+5
View File
@@ -594,6 +594,8 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
this->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
} else if (event->button() == Qt::MiddleButton) {
InputCommon::GetMotionEmu()->BeginLook(pos.x(), pos.y());
}
emit MouseActivity();
}
@@ -607,6 +609,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
const auto [x, y] = ScaleTouch(pos);
this->TouchMoved(x, y);
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
InputCommon::GetMotionEmu()->Look(pos.x(), pos.y());
emit MouseActivity();
}
@@ -619,6 +622,8 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
this->TouchReleased();
else if (event->button() == Qt::RightButton)
InputCommon::GetMotionEmu()->EndTilt();
else if (event->button() == Qt::MiddleButton)
InputCommon::GetMotionEmu()->EndLook();
emit MouseActivity();
}
@@ -1,6 +1,6 @@
// Copyright 2018-2026 Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the misc/licenses/gplv2.txt file included.
// Licensed under GPLv3 or any later version
// Refer to the LICENSE.txt file included.
#include <array>
#include <QCloseEvent>
@@ -76,7 +76,8 @@ void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) {
}
constexpr std::array<std::pair<const char*, const char*>, 3> MotionProviders = {{
{"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")},
{"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch",
"Mouse (Right Click for Tilt, Middle Click for Look)")},
{"cemuhookudp", "CemuhookUDP"},
{"sdl", "SDL"},
}};
+70 -7
View File
@@ -1,9 +1,10 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the misc/licenses/gplv2.txt file included.
// Copyright 2017-2026 Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv3 or any later version
// Refer to the LICENSE.txt file included.
#include <algorithm>
#include <chrono>
#include <cmath>
#include <mutex>
#include <thread>
#include <tuple>
@@ -34,9 +35,21 @@ public:
void BeginTilt(int x, int y) {
mouse_origin = Common::MakeVec(x, y);
std::lock_guard guard{tilt_mutex};
look_direction = Common::MakeVec(0.0f, 0.0f);
look_mode = false;
is_looking = false;
is_tilting = true;
}
void BeginLook(int x, int y) {
look_origin = Common::MakeVec(x, y);
std::lock_guard guard{tilt_mutex};
previous_look_direction = look_direction;
look_mode = true;
is_looking = true;
}
void Tilt(int x, int y) {
auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
if (is_tilting) {
@@ -51,12 +64,25 @@ public:
}
}
void Look(int x, int y) {
if (is_looking) {
const auto mouse_move = (Common::MakeVec(x, y) - look_origin).Cast<float>();
std::lock_guard guard{tilt_mutex};
look_direction = previous_look_direction + mouse_move;
}
}
void EndTilt() {
std::lock_guard guard{tilt_mutex};
tilt_angle = 0;
is_tilting = false;
}
void EndLook() {
std::lock_guard guard{tilt_mutex};
is_looking = false;
}
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() {
std::lock_guard guard{status_mutex};
return status;
@@ -68,13 +94,18 @@ private:
const float sensitivity;
Common::Vec2<int> mouse_origin;
Common::Vec2<int> look_origin;
std::mutex tilt_mutex;
Common::Vec2<float> tilt_direction;
Common::Vec2<float> look_direction = Common::MakeVec(0.0f, 0.0f);
Common::Vec2<float> previous_look_direction = Common::MakeVec(0.0f, 0.0f);
float tilt_angle = 0;
float tilt_clamp = 90;
bool is_tilting = false;
bool is_looking = false;
bool look_mode = false;
Common::Event shutdown_event;
@@ -87,7 +118,7 @@ private:
void MotionEmuThread() {
auto update_time = std::chrono::steady_clock::now();
Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0);
Common::Quaternion<float> q = {{0.0f, 0.0f, 0.0f}, 1.0f};
Common::Quaternion<float> old_q;
while (!shutdown_event.WaitUntil(update_time)) {
@@ -97,9 +128,23 @@ private:
{
std::lock_guard guard{tilt_mutex};
// Find the quaternion describing current 3DS tilting
q = Common::MakeQuaternion(
Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle);
if (!look_mode) {
// Find the quaternion describing current 3DS tilting
q = Common::MakeQuaternion(
Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle);
} else {
// Find the quaternion describing current 3DS looking
float p =
-1.0f * ((Common::PI * 0.25f) +
Common::PI * (-look_direction.y * sensitivity * 100.0f) / 360.0f);
float y = Common::PI * (-look_direction.x * sensitivity * 100.0f) / 360.0f;
float qw = std::cos(p) * std::cos(y);
float qx = std::sin(p) * std::cos(y);
float qy = std::cos(p) * std::sin(y);
float qz = -1.0f * std::sin(p) * std::sin(y);
q = {Common::MakeVec(qx, qy, qz), qw};
}
}
auto inv_q = q.Inverse();
@@ -158,16 +203,34 @@ void MotionEmu::BeginTilt(int x, int y) {
}
}
void MotionEmu::BeginLook(int x, int y) {
if (auto ptr = current_device.lock()) {
ptr->BeginLook(x, y);
}
}
void MotionEmu::Tilt(int x, int y) {
if (auto ptr = current_device.lock()) {
ptr->Tilt(x, y);
}
}
void MotionEmu::Look(int x, int y) {
if (auto ptr = current_device.lock()) {
ptr->Look(x, y);
}
}
void MotionEmu::EndTilt() {
if (auto ptr = current_device.lock()) {
ptr->EndTilt();
}
}
void MotionEmu::EndLook() {
if (auto ptr = current_device.lock()) {
ptr->EndLook();
}
}
} // namespace InputCommon
+25 -4
View File
@@ -1,6 +1,6 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the misc/licenses/gplv2.txt file included.
// Copyright 2017-2026 Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv3 or any later version
// Refer to the LICENSE.txt file included.
#pragma once
@@ -21,7 +21,7 @@ public:
std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
/**
* Signals that a motion sensor tilt has begun.
* Signals that a motion sensor tilt has begun. This also leaves look mode.
* @param x the x-coordinate of the cursor
* @param y the y-coordinate of the cursor
*/
@@ -39,6 +39,27 @@ public:
*/
void EndTilt();
/**
* Signals that a motion sensor look has begun. The device is held upright and the cursor
* movement rotates it in pitch and yaw, continuing from the previous look orientation.
* @param x the x-coordinate of the cursor
* @param y the y-coordinate of the cursor
*/
void BeginLook(int x, int y);
/**
* Signals that a motion sensor look is occurring.
* @param x the x-coordinate of the cursor
* @param y the y-coordinate of the cursor
*/
void Look(int x, int y);
/**
* Signals that a motion sensor look has ended. The look orientation is kept until the next
* tilt begins.
*/
void EndLook();
private:
std::weak_ptr<MotionEmuDevice> current_device;
};