motion_emu.cpp 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/math_util.h"
  5. #include "common/quaternion.h"
  6. #include "core/frontend/emu_window.h"
  7. #include "core/frontend/motion_emu.h"
  8. namespace Motion {
  9. static constexpr int update_millisecond = 100;
  10. static constexpr auto update_duration =
  11. std::chrono::duration_cast<std::chrono::steady_clock::duration>(
  12. std::chrono::milliseconds(update_millisecond));
  13. MotionEmu::MotionEmu(EmuWindow& emu_window)
  14. : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
  15. MotionEmu::~MotionEmu() {
  16. if (motion_emu_thread.joinable()) {
  17. shutdown_event.Set();
  18. motion_emu_thread.join();
  19. }
  20. }
  21. void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
  22. auto update_time = std::chrono::steady_clock::now();
  23. Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
  24. Math::Quaternion<float> old_q;
  25. while (!shutdown_event.WaitUntil(update_time)) {
  26. update_time += update_duration;
  27. old_q = q;
  28. {
  29. std::lock_guard<std::mutex> guard(tilt_mutex);
  30. // Find the quaternion describing current 3DS tilting
  31. q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
  32. tilt_angle);
  33. }
  34. auto inv_q = q.Inverse();
  35. // Set the gravity vector in world space
  36. auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
  37. // Find the angular rate vector in world space
  38. auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
  39. angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
  40. // Transform the two vectors from world space to 3DS space
  41. gravity = QuaternionRotate(inv_q, gravity);
  42. angular_rate = QuaternionRotate(inv_q, angular_rate);
  43. // Update the sensor state
  44. emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
  45. emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
  46. }
  47. }
  48. void MotionEmu::BeginTilt(int x, int y) {
  49. mouse_origin = Math::MakeVec(x, y);
  50. is_tilting = true;
  51. }
  52. void MotionEmu::Tilt(int x, int y) {
  53. constexpr float SENSITIVITY = 0.01f;
  54. auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
  55. if (is_tilting) {
  56. std::lock_guard<std::mutex> guard(tilt_mutex);
  57. if (mouse_move.x == 0 && mouse_move.y == 0) {
  58. tilt_angle = 0;
  59. } else {
  60. tilt_direction = mouse_move.Cast<float>();
  61. tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
  62. MathUtil::PI * 0.5f);
  63. }
  64. }
  65. }
  66. void MotionEmu::EndTilt() {
  67. std::lock_guard<std::mutex> guard(tilt_mutex);
  68. tilt_angle = 0;
  69. is_tilting = false;
  70. }
  71. } // namespace Motion