summaryrefslogtreecommitdiff
path: root/src/input_common/motion_emu.cpp
diff options
context:
space:
mode:
authorGravatar wwylele2017-08-07 00:04:06 +0300
committerGravatar wwylele2017-08-11 11:05:08 +0300
commit188194908c2785bd1e03485941b9148777cdddd7 (patch)
treeab6cd04195f5e18bd1e7dd21a7c2896066827a6f /src/input_common/motion_emu.cpp
parentHID: use MotionDevice for Accelerometer and Gyroscope (diff)
downloadyuzu-188194908c2785bd1e03485941b9148777cdddd7.tar.gz
yuzu-188194908c2785bd1e03485941b9148777cdddd7.tar.xz
yuzu-188194908c2785bd1e03485941b9148777cdddd7.zip
move MotionEmu from core/frontend to input_common as a InputDevice
Diffstat (limited to 'src/input_common/motion_emu.cpp')
-rw-r--r--src/input_common/motion_emu.cpp159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
new file mode 100644
index 000000000..65e80529d
--- /dev/null
+++ b/src/input_common/motion_emu.cpp
@@ -0,0 +1,159 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/math_util.h"
6#include "common/quaternion.h"
7#include "input_common/motion_emu.h"
8
9namespace InputCommon {
10
11// Implementation class of the motion emulation device
12class MotionEmuDevice {
13public:
14 MotionEmuDevice(int update_millisecond, float sensitivity)
15 : update_millisecond(update_millisecond),
16 update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
17 std::chrono::milliseconds(update_millisecond))),
18 sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
19
20 ~MotionEmuDevice() {
21 if (motion_emu_thread.joinable()) {
22 shutdown_event.Set();
23 motion_emu_thread.join();
24 }
25 }
26
27 void BeginTilt(int x, int y) {
28 mouse_origin = Math::MakeVec(x, y);
29 is_tilting = true;
30 }
31
32 void Tilt(int x, int y) {
33 auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
34 if (is_tilting) {
35 std::lock_guard<std::mutex> guard(tilt_mutex);
36 if (mouse_move.x == 0 && mouse_move.y == 0) {
37 tilt_angle = 0;
38 } else {
39 tilt_direction = mouse_move.Cast<float>();
40 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * sensitivity, 0.0f,
41 MathUtil::PI * 0.5f);
42 }
43 }
44 }
45
46 void EndTilt() {
47 std::lock_guard<std::mutex> guard(tilt_mutex);
48 tilt_angle = 0;
49 is_tilting = false;
50 }
51
52 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() {
53 std::lock_guard<std::mutex> guard(status_mutex);
54 return status;
55 }
56
57private:
58 const int update_millisecond;
59 const std::chrono::steady_clock::duration update_duration;
60 const float sensitivity;
61
62 Math::Vec2<int> mouse_origin;
63
64 std::mutex tilt_mutex;
65 Math::Vec2<float> tilt_direction;
66 float tilt_angle = 0;
67
68 bool is_tilting = false;
69
70 Common::Event shutdown_event;
71 std::thread motion_emu_thread;
72
73 std::tuple<Math::Vec3<float>, Math::Vec3<float>> status;
74 std::mutex status_mutex;
75
76 void MotionEmuThread() {
77 auto update_time = std::chrono::steady_clock::now();
78 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
79 Math::Quaternion<float> old_q;
80
81 while (!shutdown_event.WaitUntil(update_time)) {
82 update_time += update_duration;
83 old_q = q;
84
85 {
86 std::lock_guard<std::mutex> guard(tilt_mutex);
87
88 // Find the quaternion describing current 3DS tilting
89 q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
90 tilt_angle);
91 }
92
93 auto inv_q = q.Inverse();
94
95 // Set the gravity vector in world space
96 auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
97
98 // Find the angular rate vector in world space
99 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
100 angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
101
102 // Transform the two vectors from world space to 3DS space
103 gravity = QuaternionRotate(inv_q, gravity);
104 angular_rate = QuaternionRotate(inv_q, angular_rate);
105
106 // Update the sensor state
107 {
108 std::lock_guard<std::mutex> guard(status_mutex);
109 status = std::make_tuple(gravity, angular_rate);
110 }
111 }
112 }
113};
114
115// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as
116// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory
117// can forward all the inputs to the implementation only when it is valid.
118class MotionEmuDeviceWrapper : public Input::MotionDevice {
119public:
120 MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
121 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
122 }
123
124 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const {
125 return device->GetStatus();
126 }
127
128 std::shared_ptr<MotionEmuDevice> device;
129};
130
131std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
132 int update_period = params.Get("update_period", 100);
133 float sensitivity = params.Get("sensitivity", 0.01f);
134 auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
135 // Previously created device is disconnected here. Having two motion devices for 3DS is not
136 // expected.
137 current_device = device_wrapper->device;
138 return std::move(device_wrapper);
139}
140
141void MotionEmu::BeginTilt(int x, int y) {
142 if (auto ptr = current_device.lock()) {
143 ptr->BeginTilt(x, y);
144 }
145}
146
147void MotionEmu::Tilt(int x, int y) {
148 if (auto ptr = current_device.lock()) {
149 ptr->Tilt(x, y);
150 }
151}
152
153void MotionEmu::EndTilt() {
154 if (auto ptr = current_device.lock()) {
155 ptr->EndTilt();
156 }
157}
158
159} // namespace InputCommon