move MotionEmu from core/frontend to input_common as a InputDevice
This commit is contained in:
parent
867eabd6b7
commit
188194908c
17 changed files with 221 additions and 244 deletions
|
@ -62,29 +62,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
|||
TouchPressed(framebuffer_x, framebuffer_y);
|
||||
}
|
||||
|
||||
void EmuWindow::AccelerometerChanged(float x, float y, float z) {
|
||||
constexpr float coef = 512;
|
||||
|
||||
std::lock_guard<std::mutex> lock(accel_mutex);
|
||||
|
||||
// TODO(wwylele): do a time stretch as it in GyroscopeChanged
|
||||
// The time stretch formula should be like
|
||||
// stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
|
||||
accel_x = static_cast<s16>(x * coef);
|
||||
accel_y = static_cast<s16>(y * coef);
|
||||
accel_z = static_cast<s16>(z * coef);
|
||||
}
|
||||
|
||||
void EmuWindow::GyroscopeChanged(float x, float y, float z) {
|
||||
constexpr float FULL_FPS = 60;
|
||||
float coef = GetGyroscopeRawToDpsCoefficient();
|
||||
float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
|
||||
std::lock_guard<std::mutex> lock(gyro_mutex);
|
||||
gyro_x = static_cast<s16>(x * coef * stretch);
|
||||
gyro_y = static_cast<s16>(y * coef * stretch);
|
||||
gyro_z = static_cast<s16>(z * coef * stretch);
|
||||
}
|
||||
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
|
||||
Layout::FramebufferLayout layout;
|
||||
if (Settings::values.custom_layout == true) {
|
||||
|
|
|
@ -68,27 +68,6 @@ public:
|
|||
*/
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
|
||||
/**
|
||||
* Signal accelerometer state has changed.
|
||||
* @param x X-axis accelerometer value
|
||||
* @param y Y-axis accelerometer value
|
||||
* @param z Z-axis accelerometer value
|
||||
* @note all values are in unit of g (gravitational acceleration).
|
||||
* e.g. x = 1.0 means 9.8m/s^2 in x direction.
|
||||
* @see GetAccelerometerState for axis explanation.
|
||||
*/
|
||||
void AccelerometerChanged(float x, float y, float z);
|
||||
|
||||
/**
|
||||
* Signal gyroscope state has changed.
|
||||
* @param x X-axis accelerometer value
|
||||
* @param y Y-axis accelerometer value
|
||||
* @param z Z-axis accelerometer value
|
||||
* @note all values are in deg/sec.
|
||||
* @see GetGyroscopeState for axis explanation.
|
||||
*/
|
||||
void GyroscopeChanged(float x, float y, float z);
|
||||
|
||||
/**
|
||||
* Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
|
||||
* @note This should be called by the core emu thread to get a state set by the window thread.
|
||||
|
@ -100,52 +79,6 @@ public:
|
|||
return std::make_tuple(touch_x, touch_y, touch_pressed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current accelerometer state (acceleration along each three axis).
|
||||
* Axis explained:
|
||||
* +x is the same direction as LEFT on D-pad.
|
||||
* +y is normal to the touch screen, pointing outward.
|
||||
* +z is the same direction as UP on D-pad.
|
||||
* Units:
|
||||
* 1 unit of return value = 1/512 g (measured by hw test),
|
||||
* where g is the gravitational acceleration (9.8 m/sec2).
|
||||
* @note This should be called by the core emu thread to get a state set by the window thread.
|
||||
* @return std::tuple of (x, y, z)
|
||||
*/
|
||||
std::tuple<s16, s16, s16> GetAccelerometerState() {
|
||||
std::lock_guard<std::mutex> lock(accel_mutex);
|
||||
return std::make_tuple(accel_x, accel_y, accel_z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current gyroscope state (angular rates about each three axis).
|
||||
* Axis explained:
|
||||
* +x is the same direction as LEFT on D-pad.
|
||||
* +y is normal to the touch screen, pointing outward.
|
||||
* +z is the same direction as UP on D-pad.
|
||||
* Orientation is determined by right-hand rule.
|
||||
* Units:
|
||||
* 1 unit of return value = (1/coef) deg/sec,
|
||||
* where coef is the return value of GetGyroscopeRawToDpsCoefficient().
|
||||
* @note This should be called by the core emu thread to get a state set by the window thread.
|
||||
* @return std::tuple of (x, y, z)
|
||||
*/
|
||||
std::tuple<s16, s16, s16> GetGyroscopeState() {
|
||||
std::lock_guard<std::mutex> lock(gyro_mutex);
|
||||
return std::make_tuple(gyro_x, gyro_y, gyro_z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the coefficient for units conversion of gyroscope state.
|
||||
* The conversion formula is r = coefficient * v,
|
||||
* where v is angular rate in deg/sec,
|
||||
* and r is the gyroscope state.
|
||||
* @return float-type coefficient
|
||||
*/
|
||||
f32 GetGyroscopeRawToDpsCoefficient() const {
|
||||
return 14.375f; // taken from hw test, and gyroscope's document
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns currently active configuration.
|
||||
* @note Accesses to the returned object need not be consistent because it may be modified in
|
||||
|
@ -187,12 +120,6 @@ protected:
|
|||
touch_x = 0;
|
||||
touch_y = 0;
|
||||
touch_pressed = false;
|
||||
accel_x = 0;
|
||||
accel_y = -512;
|
||||
accel_z = 0;
|
||||
gyro_x = 0;
|
||||
gyro_y = 0;
|
||||
gyro_z = 0;
|
||||
}
|
||||
virtual ~EmuWindow() {}
|
||||
|
||||
|
@ -255,16 +182,6 @@ private:
|
|||
u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
|
||||
u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
|
||||
|
||||
std::mutex accel_mutex;
|
||||
s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
|
||||
s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
|
||||
s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
|
||||
|
||||
std::mutex gyro_mutex;
|
||||
s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
|
||||
s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
|
||||
s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
|
||||
|
||||
/**
|
||||
* Clip the provided coordinates to be inside the touchscreen area.
|
||||
*/
|
||||
|
|
|
@ -112,16 +112,15 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
|
|||
* A motion device is an input device that returns a tuple of accelerometer state vector and
|
||||
* gyroscope state vector.
|
||||
*
|
||||
* For accelerometer state vector:
|
||||
* For both vectors:
|
||||
* x+ is the same direction as LEFT on D-pad.
|
||||
* y+ is normal to the touch screen, pointing outward.
|
||||
* z+ is the same direction as UP on D-pad.
|
||||
* Units: measured in unit of gravitational acceleration
|
||||
*
|
||||
* For accelerometer state vector
|
||||
* Units: g (gravitational acceleration)
|
||||
*
|
||||
* For gyroscope state vector:
|
||||
* x+ is the same direction as LEFT on D-pad.
|
||||
* y+ is normal to the touch screen, pointing outward.
|
||||
* z+ is the same direction as UP on D-pad.
|
||||
* Orientation is determined by right-hand rule.
|
||||
* Units: deg/sec
|
||||
*/
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/math_util.h"
|
||||
#include "common/quaternion.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/motion_emu.h"
|
||||
|
||||
namespace Motion {
|
||||
|
||||
static constexpr int update_millisecond = 100;
|
||||
static constexpr auto update_duration =
|
||||
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
|
||||
std::chrono::milliseconds(update_millisecond));
|
||||
|
||||
MotionEmu::MotionEmu(EmuWindow& emu_window)
|
||||
: motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
|
||||
|
||||
MotionEmu::~MotionEmu() {
|
||||
if (motion_emu_thread.joinable()) {
|
||||
shutdown_event.Set();
|
||||
motion_emu_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
|
||||
auto update_time = std::chrono::steady_clock::now();
|
||||
Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
|
||||
Math::Quaternion<float> old_q;
|
||||
|
||||
while (!shutdown_event.WaitUntil(update_time)) {
|
||||
update_time += update_duration;
|
||||
old_q = q;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(tilt_mutex);
|
||||
|
||||
// Find the quaternion describing current 3DS tilting
|
||||
q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
|
||||
tilt_angle);
|
||||
}
|
||||
|
||||
auto inv_q = q.Inverse();
|
||||
|
||||
// Set the gravity vector in world space
|
||||
auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
|
||||
|
||||
// Find the angular rate vector in world space
|
||||
auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
|
||||
angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
|
||||
|
||||
// Transform the two vectors from world space to 3DS space
|
||||
gravity = QuaternionRotate(inv_q, gravity);
|
||||
angular_rate = QuaternionRotate(inv_q, angular_rate);
|
||||
|
||||
// Update the sensor state
|
||||
emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
|
||||
emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
|
||||
}
|
||||
}
|
||||
|
||||
void MotionEmu::BeginTilt(int x, int y) {
|
||||
mouse_origin = Math::MakeVec(x, y);
|
||||
is_tilting = true;
|
||||
}
|
||||
|
||||
void MotionEmu::Tilt(int x, int y) {
|
||||
constexpr float SENSITIVITY = 0.01f;
|
||||
auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
|
||||
if (is_tilting) {
|
||||
std::lock_guard<std::mutex> guard(tilt_mutex);
|
||||
if (mouse_move.x == 0 && mouse_move.y == 0) {
|
||||
tilt_angle = 0;
|
||||
} else {
|
||||
tilt_direction = mouse_move.Cast<float>();
|
||||
tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
|
||||
MathUtil::PI * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MotionEmu::EndTilt() {
|
||||
std::lock_guard<std::mutex> guard(tilt_mutex);
|
||||
tilt_angle = 0;
|
||||
is_tilting = false;
|
||||
}
|
||||
|
||||
} // namespace Motion
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include "common/thread.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
class EmuWindow;
|
||||
|
||||
namespace Motion {
|
||||
|
||||
class MotionEmu final {
|
||||
public:
|
||||
MotionEmu(EmuWindow& emu_window);
|
||||
~MotionEmu();
|
||||
|
||||
/**
|
||||
* Signals that a motion sensor tilt has begun.
|
||||
* @param x the x-coordinate of the cursor
|
||||
* @param y the y-coordinate of the cursor
|
||||
*/
|
||||
void BeginTilt(int x, int y);
|
||||
|
||||
/**
|
||||
* Signals that a motion sensor tilt is occurring.
|
||||
* @param x the x-coordinate of the cursor
|
||||
* @param y the y-coordinate of the cursor
|
||||
*/
|
||||
void Tilt(int x, int y);
|
||||
|
||||
/**
|
||||
* Signals that a motion sensor tilt has ended.
|
||||
*/
|
||||
void EndTilt();
|
||||
|
||||
private:
|
||||
Math::Vec2<int> mouse_origin;
|
||||
|
||||
std::mutex tilt_mutex;
|
||||
Math::Vec2<float> tilt_direction;
|
||||
float tilt_angle = 0;
|
||||
|
||||
bool is_tilting = false;
|
||||
|
||||
Common::Event shutdown_event;
|
||||
std::thread motion_emu_thread;
|
||||
|
||||
void MotionEmuThread(EmuWindow& emu_window);
|
||||
};
|
||||
|
||||
} // namespace Motion
|
Loading…
Add table
Add a link
Reference in a new issue