summaryrefslogtreecommitdiff
path: root/src/core/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/frontend')
-rw-r--r--src/core/frontend/emu_window.cpp22
-rw-r--r--src/core/frontend/emu_window.h49
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
4 files changed, 204 insertions, 8 deletions
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index f6f90f9e1..13c7f3de2 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -5,6 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <cmath> 6#include <cmath>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/profiler_reporting.h"
8#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
9#include "core/frontend/key_map.h" 10#include "core/frontend/key_map.h"
10#include "video_core/video_core.h" 11#include "video_core/video_core.h"
@@ -89,6 +90,27 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
89 TouchPressed(framebuffer_x, framebuffer_y); 90 TouchPressed(framebuffer_x, framebuffer_y);
90} 91}
91 92
93void EmuWindow::AccelerometerChanged(float x, float y, float z) {
94 constexpr float coef = 512;
95
96 // TODO(wwylele): do a time stretch as it in GyroscopeChanged
97 // The time stretch formula should be like
98 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
99 accel_x = x * coef;
100 accel_y = y * coef;
101 accel_z = z * coef;
102}
103
104void EmuWindow::GyroscopeChanged(float x, float y, float z) {
105 constexpr float FULL_FPS = 60;
106 float coef = GetGyroscopeRawToDpsCoefficient();
107 float stretch =
108 FULL_FPS / Common::Profiling::GetTimingResultsAggregator()->GetAggregatedResults().fps;
109 gyro_x = x * coef * stretch;
110 gyro_y = y * coef * stretch;
111 gyro_z = z * coef * stretch;
112}
113
92void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { 114void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
93 Layout::FramebufferLayout layout; 115 Layout::FramebufferLayout layout;
94 switch (Settings::values.layout_option) { 116 switch (Settings::values.layout_option) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 835c4d500..2cdbf1742 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -93,6 +93,27 @@ public:
93 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 93 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
94 94
95 /** 95 /**
96 * Signal accelerometer state has changed.
97 * @param x X-axis accelerometer value
98 * @param y Y-axis accelerometer value
99 * @param z Z-axis accelerometer value
100 * @note all values are in unit of g (gravitational acceleration).
101 * e.g. x = 1.0 means 9.8m/s^2 in x direction.
102 * @see GetAccelerometerState for axis explanation.
103 */
104 void AccelerometerChanged(float x, float y, float z);
105
106 /**
107 * Signal gyroscope state has changed.
108 * @param x X-axis accelerometer value
109 * @param y Y-axis accelerometer value
110 * @param z Z-axis accelerometer value
111 * @note all values are in deg/sec.
112 * @see GetGyroscopeState for axis explanation.
113 */
114 void GyroscopeChanged(float x, float y, float z);
115
116 /**
96 * Gets the current pad state (which buttons are pressed). 117 * Gets the current pad state (which buttons are pressed).
97 * @note This should be called by the core emu thread to get a state set by the window thread. 118 * @note This should be called by the core emu thread to get a state set by the window thread.
98 * @note This doesn't include analog input like circle pad direction 119 * @note This doesn't include analog input like circle pad direction
@@ -134,12 +155,11 @@ public:
134 * 1 unit of return value = 1/512 g (measured by hw test), 155 * 1 unit of return value = 1/512 g (measured by hw test),
135 * where g is the gravitational acceleration (9.8 m/sec2). 156 * where g is the gravitational acceleration (9.8 m/sec2).
136 * @note This should be called by the core emu thread to get a state set by the window thread. 157 * @note This should be called by the core emu thread to get a state set by the window thread.
137 * @todo Implement accelerometer input in front-end. 158 * @todo Fix this function to be thread-safe.
138 * @return std::tuple of (x, y, z) 159 * @return std::tuple of (x, y, z)
139 */ 160 */
140 std::tuple<s16, s16, s16> GetAccelerometerState() const { 161 std::tuple<s16, s16, s16> GetAccelerometerState() {
141 // stubbed 162 return std::make_tuple(accel_x, accel_y, accel_z);
142 return std::make_tuple(0, -512, 0);
143 } 163 }
144 164
145 /** 165 /**
@@ -153,12 +173,11 @@ public:
153 * 1 unit of return value = (1/coef) deg/sec, 173 * 1 unit of return value = (1/coef) deg/sec,
154 * where coef is the return value of GetGyroscopeRawToDpsCoefficient(). 174 * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
155 * @note This should be called by the core emu thread to get a state set by the window thread. 175 * @note This should be called by the core emu thread to get a state set by the window thread.
156 * @todo Implement gyroscope input in front-end. 176 * @todo Fix this function to be thread-safe.
157 * @return std::tuple of (x, y, z) 177 * @return std::tuple of (x, y, z)
158 */ 178 */
159 std::tuple<s16, s16, s16> GetGyroscopeState() const { 179 std::tuple<s16, s16, s16> GetGyroscopeState() {
160 // stubbed 180 return std::make_tuple(gyro_x, gyro_y, gyro_z);
161 return std::make_tuple(0, 0, 0);
162 } 181 }
163 182
164 /** 183 /**
@@ -216,6 +235,12 @@ protected:
216 circle_pad_x = 0; 235 circle_pad_x = 0;
217 circle_pad_y = 0; 236 circle_pad_y = 0;
218 touch_pressed = false; 237 touch_pressed = false;
238 accel_x = 0;
239 accel_y = -512;
240 accel_z = 0;
241 gyro_x = 0;
242 gyro_y = 0;
243 gyro_z = 0;
219 } 244 }
220 virtual ~EmuWindow() {} 245 virtual ~EmuWindow() {}
221 246
@@ -281,6 +306,14 @@ private:
281 s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) 306 s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
282 s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) 307 s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
283 308
309 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
310 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
311 s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
312
313 s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
314 s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
315 s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
316
284 /** 317 /**
285 * Clip the provided coordinates to be inside the touchscreen area. 318 * Clip the provided coordinates to be inside the touchscreen area.
286 */ 319 */
diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp
new file mode 100644
index 000000000..9a5b3185d
--- /dev/null
+++ b/src/core/frontend/motion_emu.cpp
@@ -0,0 +1,89 @@
1// Copyright 2016 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 "core/frontend/emu_window.h"
8#include "core/frontend/motion_emu.h"
9
10namespace Motion {
11
12static constexpr int update_millisecond = 100;
13static constexpr auto update_duration =
14 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
15 std::chrono::milliseconds(update_millisecond));
16
17MotionEmu::MotionEmu(EmuWindow& emu_window)
18 : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
19
20MotionEmu::~MotionEmu() {
21 if (motion_emu_thread.joinable()) {
22 shutdown_event.Set();
23 motion_emu_thread.join();
24 }
25}
26
27void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
28 auto update_time = std::chrono::steady_clock::now();
29 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
30 Math::Quaternion<float> old_q;
31
32 while (!shutdown_event.WaitUntil(update_time)) {
33 update_time += update_duration;
34 old_q = q;
35
36 {
37 std::lock_guard<std::mutex> guard(tilt_mutex);
38
39 // Find the quaternion describing current 3DS tilting
40 q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
41 tilt_angle);
42 }
43
44 auto inv_q = q.Inverse();
45
46 // Set the gravity vector in world space
47 auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
48
49 // Find the angular rate vector in world space
50 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
51 angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
52
53 // Transform the two vectors from world space to 3DS space
54 gravity = QuaternionRotate(inv_q, gravity);
55 angular_rate = QuaternionRotate(inv_q, angular_rate);
56
57 // Update the sensor state
58 emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
59 emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
60 }
61}
62
63void MotionEmu::BeginTilt(int x, int y) {
64 mouse_origin = Math::MakeVec(x, y);
65 is_tilting = true;
66}
67
68void MotionEmu::Tilt(int x, int y) {
69 constexpr float SENSITIVITY = 0.01f;
70 auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
71 if (is_tilting) {
72 std::lock_guard<std::mutex> guard(tilt_mutex);
73 if (mouse_move.x == 0 && mouse_move.y == 0) {
74 tilt_angle = 0;
75 } else {
76 tilt_direction = mouse_move.Cast<float>();
77 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
78 MathUtil::PI * 0.5f);
79 }
80 }
81}
82
83void MotionEmu::EndTilt() {
84 std::lock_guard<std::mutex> guard(tilt_mutex);
85 tilt_angle = 0;
86 is_tilting = false;
87}
88
89} // namespace Motion
diff --git a/src/core/frontend/motion_emu.h b/src/core/frontend/motion_emu.h
new file mode 100644
index 000000000..99d41a726
--- /dev/null
+++ b/src/core/frontend/motion_emu.h
@@ -0,0 +1,52 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include "common/thread.h"
7#include "common/vector_math.h"
8
9class EmuWindow;
10
11namespace Motion {
12
13class MotionEmu final {
14public:
15 MotionEmu(EmuWindow& emu_window);
16 ~MotionEmu();
17
18 /**
19 * Signals that a motion sensor tilt has begun.
20 * @param x the x-coordinate of the cursor
21 * @param y the y-coordinate of the cursor
22 */
23 void BeginTilt(int x, int y);
24
25 /**
26 * Signals that a motion sensor tilt is occurring.
27 * @param x the x-coordinate of the cursor
28 * @param y the y-coordinate of the cursor
29 */
30 void Tilt(int x, int y);
31
32 /**
33 * Signals that a motion sensor tilt has ended.
34 */
35 void EndTilt();
36
37private:
38 Math::Vec2<int> mouse_origin;
39
40 std::mutex tilt_mutex;
41 Math::Vec2<float> tilt_direction;
42 float tilt_angle = 0;
43
44 bool is_tilting = false;
45
46 Common::Event shutdown_event;
47 std::thread motion_emu_thread;
48
49 void MotionEmuThread(EmuWindow& emu_window);
50};
51
52} // namespace Motion