Browse Source
Merge pull request #2861 from wwylele/motion-refactor
Merge pull request #2861 from wwylele/motion-refactor
Refactor MotionEmu into a InputDevicence_cpp
committed by
GitHub
20 changed files with 302 additions and 277 deletions
-
3src/citra/config.cpp
-
8src/citra/default_ini.h
-
10src/citra/emu_window/emu_window_sdl2.cpp
-
4src/citra/emu_window/emu_window_sdl2.h
-
10src/citra_qt/bootmanager.cpp
-
4src/citra_qt/bootmanager.h
-
6src/citra_qt/configuration/config.cpp
-
2src/core/CMakeLists.txt
-
23src/core/frontend/emu_window.cpp
-
83src/core/frontend/emu_window.h
-
19src/core/frontend/input.h
-
89src/core/frontend/motion_emu.cpp
-
52src/core/frontend/motion_emu.h
-
32src/core/hle/service/hid/hid.cpp
-
1src/core/settings.h
-
2src/input_common/CMakeLists.txt
-
15src/input_common/main.cpp
-
5src/input_common/main.h
-
165src/input_common/motion_emu.cpp
-
46src/input_common/motion_emu.h
@ -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 |
|||
@ -0,0 +1,165 @@ |
|||
// Copyright 2017 Citra Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <chrono>
|
|||
#include <mutex>
|
|||
#include <thread>
|
|||
#include <tuple>
|
|||
#include "common/math_util.h"
|
|||
#include "common/quaternion.h"
|
|||
#include "common/thread.h"
|
|||
#include "common/vector_math.h"
|
|||
#include "input_common/motion_emu.h"
|
|||
|
|||
namespace InputCommon { |
|||
|
|||
// Implementation class of the motion emulation device
|
|||
class MotionEmuDevice { |
|||
public: |
|||
MotionEmuDevice(int update_millisecond, float sensitivity) |
|||
: update_millisecond(update_millisecond), |
|||
update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>( |
|||
std::chrono::milliseconds(update_millisecond))), |
|||
sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {} |
|||
|
|||
~MotionEmuDevice() { |
|||
if (motion_emu_thread.joinable()) { |
|||
shutdown_event.Set(); |
|||
motion_emu_thread.join(); |
|||
} |
|||
} |
|||
|
|||
void BeginTilt(int x, int y) { |
|||
mouse_origin = Math::MakeVec(x, y); |
|||
is_tilting = true; |
|||
} |
|||
|
|||
void Tilt(int x, int y) { |
|||
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 EndTilt() { |
|||
std::lock_guard<std::mutex> guard(tilt_mutex); |
|||
tilt_angle = 0; |
|||
is_tilting = false; |
|||
} |
|||
|
|||
std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() { |
|||
std::lock_guard<std::mutex> guard(status_mutex); |
|||
return status; |
|||
} |
|||
|
|||
private: |
|||
const int update_millisecond; |
|||
const std::chrono::steady_clock::duration update_duration; |
|||
const float sensitivity; |
|||
|
|||
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; |
|||
|
|||
std::tuple<Math::Vec3<float>, Math::Vec3<float>> status; |
|||
std::mutex status_mutex; |
|||
|
|||
void MotionEmuThread() { |
|||
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
|
|||
{ |
|||
std::lock_guard<std::mutex> guard(status_mutex); |
|||
status = std::make_tuple(gravity, angular_rate); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as
|
|||
// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory
|
|||
// can forward all the inputs to the implementation only when it is valid.
|
|||
class MotionEmuDeviceWrapper : public Input::MotionDevice { |
|||
public: |
|||
MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) { |
|||
device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity); |
|||
} |
|||
|
|||
std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const { |
|||
return device->GetStatus(); |
|||
} |
|||
|
|||
std::shared_ptr<MotionEmuDevice> device; |
|||
}; |
|||
|
|||
std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) { |
|||
int update_period = params.Get("update_period", 100); |
|||
float sensitivity = params.Get("sensitivity", 0.01f); |
|||
auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity); |
|||
// Previously created device is disconnected here. Having two motion devices for 3DS is not
|
|||
// expected.
|
|||
current_device = device_wrapper->device; |
|||
return std::move(device_wrapper); |
|||
} |
|||
|
|||
void MotionEmu::BeginTilt(int x, int y) { |
|||
if (auto ptr = current_device.lock()) { |
|||
ptr->BeginTilt(x, y); |
|||
} |
|||
} |
|||
|
|||
void MotionEmu::Tilt(int x, int y) { |
|||
if (auto ptr = current_device.lock()) { |
|||
ptr->Tilt(x, y); |
|||
} |
|||
} |
|||
|
|||
void MotionEmu::EndTilt() { |
|||
if (auto ptr = current_device.lock()) { |
|||
ptr->EndTilt(); |
|||
} |
|||
} |
|||
|
|||
} // namespace InputCommon
|
|||
@ -0,0 +1,46 @@ |
|||
// Copyright 2017 Citra Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "core/frontend/input.h" |
|||
|
|||
namespace InputCommon { |
|||
|
|||
class MotionEmuDevice; |
|||
|
|||
class MotionEmu : public Input::Factory<Input::MotionDevice> { |
|||
public: |
|||
/** |
|||
* Creates a motion device emulated from mouse input |
|||
* @param params contains parameters for creating the device: |
|||
* - "update_period": update period in milliseconds |
|||
* - "sensitivity": the coefficient converting mouse movement to tilting angle |
|||
*/ |
|||
std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override; |
|||
|
|||
/** |
|||
* 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: |
|||
std::weak_ptr<MotionEmuDevice> current_device; |
|||
}; |
|||
|
|||
} // namespace InputCommon |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue