| | |
| | |
| | |
| |
|
| | #include <algorithm> |
| | #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 { |
| |
|
| | |
| | class MotionEmuDevice { |
| | public: |
| | MotionEmuDevice(int update_millisecond, float sensitivity, float tilt_clamp) |
| | : update_millisecond(update_millisecond), |
| | update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>( |
| | std::chrono::milliseconds(update_millisecond))), |
| | sensitivity(sensitivity), tilt_clamp(tilt_clamp), |
| | 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 = Common::MakeVec(x, y); |
| | is_tilting = true; |
| | } |
| |
|
| | void Tilt(int x, int y) { |
| | auto mouse_move = Common::MakeVec(x, y) - mouse_origin; |
| | if (is_tilting) { |
| | std::lock_guard guard{tilt_mutex}; |
| | if (mouse_move.x == 0 && mouse_move.y == 0) { |
| | tilt_angle = 0; |
| | } else { |
| | tilt_direction = mouse_move.Cast<float>(); |
| | tilt_angle = std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, |
| | Common::PI * this->tilt_clamp / 180.0f); |
| | } |
| | } |
| | } |
| |
|
| | void EndTilt() { |
| | std::lock_guard guard{tilt_mutex}; |
| | tilt_angle = 0; |
| | is_tilting = false; |
| | } |
| |
|
| | std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() { |
| | std::lock_guard guard{status_mutex}; |
| | return status; |
| | } |
| |
|
| | private: |
| | const int update_millisecond; |
| | const std::chrono::steady_clock::duration update_duration; |
| | const float sensitivity; |
| |
|
| | Common::Vec2<int> mouse_origin; |
| |
|
| | std::mutex tilt_mutex; |
| | Common::Vec2<float> tilt_direction; |
| | float tilt_angle = 0; |
| | float tilt_clamp = 90; |
| |
|
| | bool is_tilting = false; |
| |
|
| | Common::Event shutdown_event; |
| |
|
| | std::tuple<Common::Vec3<float>, Common::Vec3<float>> status; |
| | std::mutex status_mutex; |
| |
|
| | |
| | |
| | std::thread motion_emu_thread; |
| |
|
| | void MotionEmuThread() { |
| | auto update_time = std::chrono::steady_clock::now(); |
| | Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0); |
| | Common::Quaternion<float> old_q; |
| |
|
| | while (!shutdown_event.WaitUntil(update_time)) { |
| | update_time += update_duration; |
| | old_q = q; |
| |
|
| | { |
| | std::lock_guard guard{tilt_mutex}; |
| |
|
| | |
| | q = Common::MakeQuaternion( |
| | Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle); |
| | } |
| |
|
| | auto inv_q = q.Inverse(); |
| |
|
| | |
| | auto gravity = Common::MakeVec(0.0f, -1.0f, 0.0f); |
| |
|
| | |
| | auto angular_rate = ((q - old_q) * inv_q).xyz * 2; |
| | angular_rate *= 1000 / update_millisecond / Common::PI * 180; |
| |
|
| | |
| | gravity = QuaternionRotate(inv_q, gravity); |
| | angular_rate = QuaternionRotate(inv_q, angular_rate); |
| |
|
| | |
| | { |
| | std::lock_guard guard{status_mutex}; |
| | status = std::make_tuple(gravity, angular_rate); |
| | } |
| | } |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | class MotionEmuDeviceWrapper : public Input::MotionDevice { |
| | public: |
| | MotionEmuDeviceWrapper(int update_millisecond, float sensitivity, float tilt_clamp) { |
| | device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity, tilt_clamp); |
| | } |
| |
|
| | std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { |
| | 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); |
| | float tilt_clamp = params.Get("tilt_clamp", 90.0f); |
| | auto device_wrapper = |
| | std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity, tilt_clamp); |
| | |
| | |
| | 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(); |
| | } |
| | } |
| |
|
| | } |
| |
|