summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/frontend/emu_window.cpp23
-rw-r--r--src/core/frontend/emu_window.h83
-rw-r--r--src/core/frontend/input.h19
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
-rw-r--r--src/core/hle/service/hid/hid.cpp32
-rw-r--r--src/core/settings.h1
8 files changed, 47 insertions, 254 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0a6f97e4b..53bd50eb2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,7 +33,6 @@ set(SRCS
33 frontend/camera/interface.cpp 33 frontend/camera/interface.cpp
34 frontend/emu_window.cpp 34 frontend/emu_window.cpp
35 frontend/framebuffer_layout.cpp 35 frontend/framebuffer_layout.cpp
36 frontend/motion_emu.cpp
37 gdbstub/gdbstub.cpp 36 gdbstub/gdbstub.cpp
38 hle/config_mem.cpp 37 hle/config_mem.cpp
39 hle/applets/applet.cpp 38 hle/applets/applet.cpp
@@ -226,7 +225,6 @@ set(HEADERS
226 frontend/emu_window.h 225 frontend/emu_window.h
227 frontend/framebuffer_layout.h 226 frontend/framebuffer_layout.h
228 frontend/input.h 227 frontend/input.h
229 frontend/motion_emu.h
230 gdbstub/gdbstub.h 228 gdbstub/gdbstub.h
231 hle/config_mem.h 229 hle/config_mem.h
232 hle/function_wrappers.h 230 hle/function_wrappers.h
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 4f7d54a33..60b20d4e2 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -62,29 +62,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
62 TouchPressed(framebuffer_x, framebuffer_y); 62 TouchPressed(framebuffer_x, framebuffer_y);
63} 63}
64 64
65void EmuWindow::AccelerometerChanged(float x, float y, float z) {
66 constexpr float coef = 512;
67
68 std::lock_guard<std::mutex> lock(accel_mutex);
69
70 // TODO(wwylele): do a time stretch as it in GyroscopeChanged
71 // The time stretch formula should be like
72 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
73 accel_x = static_cast<s16>(x * coef);
74 accel_y = static_cast<s16>(y * coef);
75 accel_z = static_cast<s16>(z * coef);
76}
77
78void EmuWindow::GyroscopeChanged(float x, float y, float z) {
79 constexpr float FULL_FPS = 60;
80 float coef = GetGyroscopeRawToDpsCoefficient();
81 float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
82 std::lock_guard<std::mutex> lock(gyro_mutex);
83 gyro_x = static_cast<s16>(x * coef * stretch);
84 gyro_y = static_cast<s16>(y * coef * stretch);
85 gyro_z = static_cast<s16>(z * coef * stretch);
86}
87
88void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { 65void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
89 Layout::FramebufferLayout layout; 66 Layout::FramebufferLayout layout;
90 if (Settings::values.custom_layout == true) { 67 if (Settings::values.custom_layout == true) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 9414123a4..7bdee251c 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -69,27 +69,6 @@ public:
69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
70 70
71 /** 71 /**
72 * Signal accelerometer state has changed.
73 * @param x X-axis accelerometer value
74 * @param y Y-axis accelerometer value
75 * @param z Z-axis accelerometer value
76 * @note all values are in unit of g (gravitational acceleration).
77 * e.g. x = 1.0 means 9.8m/s^2 in x direction.
78 * @see GetAccelerometerState for axis explanation.
79 */
80 void AccelerometerChanged(float x, float y, float z);
81
82 /**
83 * Signal gyroscope state has changed.
84 * @param x X-axis accelerometer value
85 * @param y Y-axis accelerometer value
86 * @param z Z-axis accelerometer value
87 * @note all values are in deg/sec.
88 * @see GetGyroscopeState for axis explanation.
89 */
90 void GyroscopeChanged(float x, float y, float z);
91
92 /**
93 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). 72 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
94 * @note This should be called by the core emu thread to get a state set by the window thread. 73 * @note This should be called by the core emu thread to get a state set by the window thread.
95 * @todo Fix this function to be thread-safe. 74 * @todo Fix this function to be thread-safe.
@@ -101,52 +80,6 @@ public:
101 } 80 }
102 81
103 /** 82 /**
104 * Gets the current accelerometer state (acceleration along each three axis).
105 * Axis explained:
106 * +x is the same direction as LEFT on D-pad.
107 * +y is normal to the touch screen, pointing outward.
108 * +z is the same direction as UP on D-pad.
109 * Units:
110 * 1 unit of return value = 1/512 g (measured by hw test),
111 * where g is the gravitational acceleration (9.8 m/sec2).
112 * @note This should be called by the core emu thread to get a state set by the window thread.
113 * @return std::tuple of (x, y, z)
114 */
115 std::tuple<s16, s16, s16> GetAccelerometerState() {
116 std::lock_guard<std::mutex> lock(accel_mutex);
117 return std::make_tuple(accel_x, accel_y, accel_z);
118 }
119
120 /**
121 * Gets the current gyroscope state (angular rates about each three axis).
122 * Axis explained:
123 * +x is the same direction as LEFT on D-pad.
124 * +y is normal to the touch screen, pointing outward.
125 * +z is the same direction as UP on D-pad.
126 * Orientation is determined by right-hand rule.
127 * Units:
128 * 1 unit of return value = (1/coef) deg/sec,
129 * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
130 * @note This should be called by the core emu thread to get a state set by the window thread.
131 * @return std::tuple of (x, y, z)
132 */
133 std::tuple<s16, s16, s16> GetGyroscopeState() {
134 std::lock_guard<std::mutex> lock(gyro_mutex);
135 return std::make_tuple(gyro_x, gyro_y, gyro_z);
136 }
137
138 /**
139 * Gets the coefficient for units conversion of gyroscope state.
140 * The conversion formula is r = coefficient * v,
141 * where v is angular rate in deg/sec,
142 * and r is the gyroscope state.
143 * @return float-type coefficient
144 */
145 f32 GetGyroscopeRawToDpsCoefficient() const {
146 return 14.375f; // taken from hw test, and gyroscope's document
147 }
148
149 /**
150 * Returns currently active configuration. 83 * Returns currently active configuration.
151 * @note Accesses to the returned object need not be consistent because it may be modified in 84 * @note Accesses to the returned object need not be consistent because it may be modified in
152 * another thread 85 * another thread
@@ -187,12 +120,6 @@ protected:
187 touch_x = 0; 120 touch_x = 0;
188 touch_y = 0; 121 touch_y = 0;
189 touch_pressed = false; 122 touch_pressed = false;
190 accel_x = 0;
191 accel_y = -512;
192 accel_z = 0;
193 gyro_x = 0;
194 gyro_y = 0;
195 gyro_z = 0;
196 } 123 }
197 virtual ~EmuWindow() {} 124 virtual ~EmuWindow() {}
198 125
@@ -255,16 +182,6 @@ private:
255 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 182 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
256 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) 183 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
257 184
258 std::mutex accel_mutex;
259 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
260 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
261 s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
262
263 std::mutex gyro_mutex;
264 s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
265 s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
266 s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
267
268 /** 185 /**
269 * Clip the provided coordinates to be inside the touchscreen area. 186 * Clip the provided coordinates to be inside the touchscreen area.
270 */ 187 */
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 0a5713dc0..5916a901d 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -11,6 +11,7 @@
11#include <utility> 11#include <utility>
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/param_package.h" 13#include "common/param_package.h"
14#include "common/vector_math.h"
14 15
15namespace Input { 16namespace Input {
16 17
@@ -107,4 +108,22 @@ using ButtonDevice = InputDevice<bool>;
107 */ 108 */
108using AnalogDevice = InputDevice<std::tuple<float, float>>; 109using AnalogDevice = InputDevice<std::tuple<float, float>>;
109 110
111/**
112 * A motion device is an input device that returns a tuple of accelerometer state vector and
113 * gyroscope state vector.
114 *
115 * For both vectors:
116 * x+ is the same direction as LEFT on D-pad.
117 * y+ is normal to the touch screen, pointing outward.
118 * z+ is the same direction as UP on D-pad.
119 *
120 * For accelerometer state vector
121 * Units: g (gravitational acceleration)
122 *
123 * For gyroscope state vector:
124 * Orientation is determined by right-hand rule.
125 * Units: deg/sec
126 */
127using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
128
110} // namespace Input 129} // namespace Input
diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp
deleted file mode 100644
index 9a5b3185d..000000000
--- a/src/core/frontend/motion_emu.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
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
deleted file mode 100644
index 99d41a726..000000000
--- a/src/core/frontend/motion_emu.h
+++ /dev/null
@@ -1,52 +0,0 @@
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
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2014b8461..31f34a7ae 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,6 +7,7 @@
7#include <cmath> 7#include <cmath>
8#include <memory> 8#include <memory>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/frontend/input.h"
@@ -50,10 +51,14 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
50constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; 51constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
51constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; 52constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
52 53
54constexpr float accelerometer_coef = 512.0f; // measured from hw test result
55constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
56
53static std::atomic<bool> is_device_reload_pending; 57static std::atomic<bool> is_device_reload_pending;
54static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 58static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
55 buttons; 59 buttons;
56static std::unique_ptr<Input::AnalogDevice> circle_pad; 60static std::unique_ptr<Input::AnalogDevice> circle_pad;
61static std::unique_ptr<Input::MotionDevice> motion_device;
57 62
58DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 63DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
59 // 30 degree and 60 degree are angular thresholds for directions 64 // 30 degree and 60 degree are angular thresholds for directions
@@ -90,6 +95,7 @@ static void LoadInputDevices() {
90 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 95 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
91 circle_pad = Input::CreateDevice<Input::AnalogDevice>( 96 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
92 Settings::values.analogs[Settings::NativeAnalog::CirclePad]); 97 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
98 motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
93} 99}
94 100
95static void UnloadInputDevices() { 101static void UnloadInputDevices() {
@@ -97,6 +103,7 @@ static void UnloadInputDevices() {
97 button.reset(); 103 button.reset();
98 } 104 }
99 circle_pad.reset(); 105 circle_pad.reset();
106 motion_device.reset();
100} 107}
101 108
102static void UpdatePadCallback(u64 userdata, int cycles_late) { 109static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -193,10 +200,19 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
193 mem->accelerometer.index = next_accelerometer_index; 200 mem->accelerometer.index = next_accelerometer_index;
194 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); 201 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
195 202
203 Math::Vec3<float> accel;
204 std::tie(accel, std::ignore) = motion_device->GetStatus();
205 accel *= accelerometer_coef;
206 // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
207 // The time stretch formula should be like
208 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
209
196 AccelerometerDataEntry& accelerometer_entry = 210 AccelerometerDataEntry& accelerometer_entry =
197 mem->accelerometer.entries[mem->accelerometer.index]; 211 mem->accelerometer.entries[mem->accelerometer.index];
198 std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = 212
199 VideoCore::g_emu_window->GetAccelerometerState(); 213 accelerometer_entry.x = static_cast<s16>(accel.x);
214 accelerometer_entry.y = static_cast<s16>(accel.y);
215 accelerometer_entry.z = static_cast<s16>(accel.z);
200 216
201 // Make up "raw" entry 217 // Make up "raw" entry
202 // TODO(wwylele): 218 // TODO(wwylele):
@@ -227,8 +243,14 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
227 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); 243 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
228 244
229 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; 245 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
230 std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = 246
231 VideoCore::g_emu_window->GetGyroscopeState(); 247 Math::Vec3<float> gyro;
248 std::tie(std::ignore, gyro) = motion_device->GetStatus();
249 double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
250 gyro *= gyroscope_coef * stretch;
251 gyroscope_entry.x = static_cast<s16>(gyro.x);
252 gyroscope_entry.y = static_cast<s16>(gyro.y);
253 gyroscope_entry.z = static_cast<s16>(gyro.z);
232 254
233 // Make up "raw" entry 255 // Make up "raw" entry
234 mem->gyroscope.raw_entry.x = gyroscope_entry.x; 256 mem->gyroscope.raw_entry.x = gyroscope_entry.x;
@@ -326,7 +348,7 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
326 348
327 cmd_buff[1] = RESULT_SUCCESS.raw; 349 cmd_buff[1] = RESULT_SUCCESS.raw;
328 350
329 f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient(); 351 f32 coef = gyroscope_coef;
330 memcpy(&cmd_buff[2], &coef, 4); 352 memcpy(&cmd_buff[2], &coef, 4);
331} 353}
332 354
diff --git a/src/core/settings.h b/src/core/settings.h
index ee16bb90a..7e15b119b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -79,6 +79,7 @@ struct Values {
79 // Controls 79 // Controls
80 std::array<std::string, NativeButton::NumButtons> buttons; 80 std::array<std::string, NativeButton::NumButtons> buttons;
81 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 81 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
82 std::string motion_device;
82 83
83 // Core 84 // Core
84 bool use_cpu_jit; 85 bool use_cpu_jit;