commit
bbfa9d0635
@ -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
|
Loading…
Reference in New Issue