Skip to content
Permalink
Browse files
Merge pull request #8747 from iwubcode/map-freelook
Support controlling Free Look via input bindings (motion controls, gamepad, etc!)
  • Loading branch information
JMC47 committed Apr 1, 2021
2 parents 06439a2 + 28e880e commit ce8e87c
Show file tree
Hide file tree
Showing 25 changed files with 420 additions and 61 deletions.
@@ -4,6 +4,8 @@

#include "Common/Matrix.h"

#include "Common/MathUtil.h"

#include <algorithm>
#include <cmath>

@@ -121,6 +123,32 @@ Vec3 operator*(const Quaternion& lhs, const Vec3& rhs)
return Vec3(result.data.x, result.data.y, result.data.z);
}

Vec3 FromQuaternionToEuler(const Quaternion& q)
{
Vec3 result;

const float qx = q.data.x;
const float qy = q.data.y;
const float qz = q.data.z;
const float qw = q.data.w;

const float sinr_cosp = 2 * (qw * qx + qy * qz);
const float cosr_cosp = 1 - 2 * (qx * qx + qy * qy);
result.x = std::atan2(sinr_cosp, cosr_cosp);

const float sinp = 2 * (qw * qy - qz * qx);
if (std::abs(sinp) >= 1)
result.y = std::copysign(MathUtil::PI / 2, sinp); // use 90 degrees if out of range
else
result.y = std::asin(sinp);

const float siny_cosp = 2 * (qw * qz + qx * qy);
const float cosy_cosp = 1 - 2 * (qy * qy + qz * qz);
result.z = std::atan2(siny_cosp, cosy_cosp);

return result;
}

Matrix33 Matrix33::Identity()
{
Matrix33 mtx = {};
@@ -359,6 +359,8 @@ class Quaternion
Quaternion operator*(Quaternion lhs, const Quaternion& rhs);
Vec3 operator*(const Quaternion& lhs, const Vec3& rhs);

Vec3 FromQuaternionToEuler(const Quaternion& q);

class Matrix33
{
public:
@@ -12,6 +12,7 @@
#include "Core/FreeLookConfig.h"

#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/InputConfig.h"

#include "VideoCommon/FreeLookCamera.h"
@@ -60,6 +61,19 @@ enum FieldOfViewButtons
DecreaseY,
};
}

namespace GyroButtons
{
enum GyroButtons
{
PitchUp,
PitchDown,
RollLeft,
RollRight,
YawLeft,
YawRight,
};
}
} // namespace

FreeLookController::FreeLookController(const unsigned int index) : m_index(index)
@@ -89,6 +103,9 @@ FreeLookController::FreeLookController(const unsigned int index) : m_index(index
m_fov_buttons->AddInput(ControllerEmu::Translate, _trans("Decrease X"));
m_fov_buttons->AddInput(ControllerEmu::Translate, _trans("Increase Y"));
m_fov_buttons->AddInput(ControllerEmu::Translate, _trans("Decrease Y"));

groups.emplace_back(m_rotation_gyro = new ControllerEmu::IMUGyroscope(
_trans("Incremental Rotation"), _trans("Incremental Rotation")));
}

std::string FreeLookController::GetName() const
@@ -125,6 +142,35 @@ void FreeLookController::LoadDefaults(const ControllerInterface& ciface)
hotkey_string({"Shift", "`Axis Z+`"}));
m_fov_buttons->SetControlExpression(FieldOfViewButtons::DecreaseY,
hotkey_string({"Shift", "`Axis Z-`"}));

#if defined HAVE_X11 && HAVE_X11
m_rotation_gyro->SetControlExpression(GyroButtons::PitchUp,
"if(`Click 3`,`RelativeMouse Y-` * 0.10, 0)");
m_rotation_gyro->SetControlExpression(GyroButtons::PitchDown,
"if(`Click 3`,`RelativeMouse Y+` * 0.10, 0)");
#else
m_rotation_gyro->SetControlExpression(GyroButtons::PitchUp,
"if(`Click 1`,`RelativeMouse Y-` * 0.10, 0)");
m_rotation_gyro->SetControlExpression(GyroButtons::PitchDown,
"if(`Click 1`,`RelativeMouse Y+` * 0.10, 0)");
#endif

m_rotation_gyro->SetControlExpression(GyroButtons::RollLeft,
"if(`Click 2`,`RelativeMouse X-` * 0.10, 0)");
m_rotation_gyro->SetControlExpression(GyroButtons::RollRight,
"if(`Click 2`,`RelativeMouse X+` * 0.10, 0)");

#if defined HAVE_X11 && HAVE_X11
m_rotation_gyro->SetControlExpression(GyroButtons::YawLeft,
"if(`Click 3`,`RelativeMouse X-` * 0.10, 0)");
m_rotation_gyro->SetControlExpression(GyroButtons::YawRight,
"if(`Click 3`,`RelativeMouse X+` * 0.10, 0)");
#else
m_rotation_gyro->SetControlExpression(GyroButtons::YawLeft,
"if(`Click 1`,`RelativeMouse X-` * 0.10, 0)");
m_rotation_gyro->SetControlExpression(GyroButtons::YawRight,
"if(`Click 1`,`RelativeMouse X+` * 0.10, 0)");
#endif
}

ControllerEmu::ControlGroup* FreeLookController::GetGroup(FreeLookGroup group) const
@@ -139,6 +185,8 @@ ControllerEmu::ControlGroup* FreeLookController::GetGroup(FreeLookGroup group) c
return m_fov_buttons;
case FreeLookGroup::Other:
return m_other_buttons;
case FreeLookGroup::Rotation:
return m_rotation_gyro;
default:
return nullptr;
}
@@ -151,41 +199,63 @@ void FreeLookController::Update()

const auto lock = GetStateLock();

float dt = 1.0;
if (m_last_free_look_rotate_time)
{
using seconds = std::chrono::duration<float, std::ratio<1>>;
dt = std::chrono::duration_cast<seconds>(std::chrono::steady_clock::now() -
*m_last_free_look_rotate_time)
.count();
}
m_last_free_look_rotate_time = std::chrono::steady_clock::now();

const auto gyro_motion_rad_velocity =
m_rotation_gyro->GetState() ? *m_rotation_gyro->GetState() : Common::Vec3{};

// Due to gyroscope implementation we need to swap the yaw and roll values
// and because of the different axis used for Wii and the PS3 motion directions,
// we need to invert the yaw and roll as well
const Common::Vec3 gyro_motion_rad_velocity_converted{
gyro_motion_rad_velocity.x, gyro_motion_rad_velocity.z * -1, gyro_motion_rad_velocity.y * -1};
const auto gyro_motion_quat =
Common::Quaternion::RotateXYZ(gyro_motion_rad_velocity_converted * dt);

g_freelook_camera.Rotate(gyro_motion_quat);
if (m_move_buttons->controls[MoveButtons::Up]->GetState<bool>())
g_freelook_camera.MoveVertical(-g_freelook_camera.GetSpeed());
g_freelook_camera.MoveVertical(-g_freelook_camera.GetSpeed() * dt);

if (m_move_buttons->controls[MoveButtons::Down]->GetState<bool>())
g_freelook_camera.MoveVertical(g_freelook_camera.GetSpeed());
g_freelook_camera.MoveVertical(g_freelook_camera.GetSpeed() * dt);

if (m_move_buttons->controls[MoveButtons::Left]->GetState<bool>())
g_freelook_camera.MoveHorizontal(g_freelook_camera.GetSpeed());
g_freelook_camera.MoveHorizontal(g_freelook_camera.GetSpeed() * dt);

if (m_move_buttons->controls[MoveButtons::Right]->GetState<bool>())
g_freelook_camera.MoveHorizontal(-g_freelook_camera.GetSpeed());
g_freelook_camera.MoveHorizontal(-g_freelook_camera.GetSpeed() * dt);

if (m_move_buttons->controls[MoveButtons::Forward]->GetState<bool>())
g_freelook_camera.MoveForward(g_freelook_camera.GetSpeed());
g_freelook_camera.MoveForward(g_freelook_camera.GetSpeed() * dt);

if (m_move_buttons->controls[MoveButtons::Backward]->GetState<bool>())
g_freelook_camera.MoveForward(-g_freelook_camera.GetSpeed());
g_freelook_camera.MoveForward(-g_freelook_camera.GetSpeed() * dt);

if (m_fov_buttons->controls[FieldOfViewButtons::IncreaseX]->GetState<bool>())
g_freelook_camera.IncreaseFovX(g_freelook_camera.GetFovStepSize());
g_freelook_camera.IncreaseFovX(g_freelook_camera.GetFovStepSize() * dt);

if (m_fov_buttons->controls[FieldOfViewButtons::DecreaseX]->GetState<bool>())
g_freelook_camera.IncreaseFovX(-1.0f * g_freelook_camera.GetFovStepSize());
g_freelook_camera.IncreaseFovX(-1.0f * g_freelook_camera.GetFovStepSize() * dt);

if (m_fov_buttons->controls[FieldOfViewButtons::IncreaseY]->GetState<bool>())
g_freelook_camera.IncreaseFovY(g_freelook_camera.GetFovStepSize());
g_freelook_camera.IncreaseFovY(g_freelook_camera.GetFovStepSize() * dt);

if (m_fov_buttons->controls[FieldOfViewButtons::DecreaseY]->GetState<bool>())
g_freelook_camera.IncreaseFovY(-1.0f * g_freelook_camera.GetFovStepSize());
g_freelook_camera.IncreaseFovY(-1.0f * g_freelook_camera.GetFovStepSize() * dt);

if (m_speed_buttons->controls[SpeedButtons::Decrease]->GetState<bool>())
g_freelook_camera.ModifySpeed(1.0f / 1.1f);
g_freelook_camera.ModifySpeed(g_freelook_camera.GetSpeed() * -0.9 * dt);

if (m_speed_buttons->controls[SpeedButtons::Increase]->GetState<bool>())
g_freelook_camera.ModifySpeed(1.1f);
g_freelook_camera.ModifySpeed(g_freelook_camera.GetSpeed() * 1.1 * dt);

if (m_speed_buttons->controls[SpeedButtons::Reset]->GetState<bool>())
g_freelook_camera.ResetSpeed();
@@ -4,6 +4,9 @@

#pragma once

#include <chrono>
#include <optional>

#include "Common/CommonTypes.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"

@@ -13,14 +16,16 @@ namespace ControllerEmu
{
class ControlGroup;
class Buttons;
class IMUGyroscope;
} // namespace ControllerEmu

enum class FreeLookGroup
{
Move,
Speed,
FieldOfView,
Other
Other,
Rotation,
};

namespace FreeLook
@@ -52,6 +57,8 @@ class FreeLookController final : public ControllerEmu::EmulatedController
ControllerEmu::Buttons* m_speed_buttons;
ControllerEmu::Buttons* m_fov_buttons;
ControllerEmu::Buttons* m_other_buttons;
ControllerEmu::IMUGyroscope* m_rotation_gyro;

const unsigned int m_index;
std::optional<std::chrono::steady_clock::time_point> m_last_free_look_rotate_time;
};
@@ -655,6 +655,7 @@ void UpdateDevices()

// Update inputs at the rate of SI
// Typically 120hz but is variable
g_controller_interface.SetCurrentInputChannel(ciface::InputChannel::SerialInterface);
g_controller_interface.UpdateInput();

// Update channels and set the status bit if there's new data
@@ -339,6 +339,7 @@ void BluetoothEmuDevice::Update()

if (now - m_last_ticks > interval)
{
g_controller_interface.SetCurrentInputChannel(ciface::InputChannel::Bluetooth);
g_controller_interface.UpdateInput();
for (auto& wiimote : m_wiimotes)
wiimote->UpdateInput();
@@ -122,6 +122,8 @@ add_executable(dolphin-emu
Config/LogWidget.h
Config/Mapping/FreeLookGeneral.cpp
Config/Mapping/FreeLookGeneral.h
Config/Mapping/FreeLookRotation.cpp
Config/Mapping/FreeLookRotation.h
Config/Mapping/GCKeyboardEmu.cpp
Config/Mapping/GCKeyboardEmu.h
Config/Mapping/GCMicrophone.cpp
@@ -0,0 +1,62 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "DolphinQt/Config/Mapping/FreeLookRotation.h"

#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>

#include "Core/FreeLookManager.h"
#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h"
#include "InputCommon/InputConfig.h"

FreeLookRotation::FreeLookRotation(MappingWindow* window) : MappingWidget(window)
{
CreateMainLayout();
}

void FreeLookRotation::CreateMainLayout()
{
m_main_layout = new QGridLayout;

auto* alternate_input_layout = new QHBoxLayout();
auto* note_label = new QLabel(
tr("Note: motion input may require configuring alternate input sources before use."));
note_label->setWordWrap(true);
auto* alternate_input_sources_button = new QPushButton(tr("Alternate Input Sources"));
alternate_input_layout->addWidget(note_label, 1);
alternate_input_layout->addWidget(alternate_input_sources_button, 0, Qt::AlignRight);
connect(alternate_input_sources_button, &QPushButton::clicked, this, [this] {
ControllerInterfaceWindow* window = new ControllerInterfaceWindow(this);
window->setAttribute(Qt::WA_DeleteOnClose, true);
window->setWindowModality(Qt::WindowModality::WindowModal);
window->show();
});
m_main_layout->addLayout(alternate_input_layout, 0, 0, 1, -1);

m_main_layout->addWidget(
CreateGroupBox(tr("Incremental Rotation (rad/sec)"),
FreeLook::GetInputGroup(GetPort(), FreeLookGroup::Rotation)),
1, 0);

setLayout(m_main_layout);
}

InputConfig* FreeLookRotation::GetConfig()
{
return FreeLook::GetInputConfig();
}

void FreeLookRotation::LoadSettings()
{
FreeLook::LoadInputConfig();
}

void FreeLookRotation::SaveSettings()
{
FreeLook::GetInputConfig()->SaveConfig();
}
@@ -0,0 +1,26 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include "DolphinQt/Config/Mapping/MappingWidget.h"

class QGridLayout;

class FreeLookRotation final : public MappingWidget
{
Q_OBJECT
public:
explicit FreeLookRotation(MappingWindow* window);

InputConfig* GetConfig() override;

private:
void LoadSettings() override;
void SaveSettings() override;
void CreateMainLayout();

// Main
QGridLayout* m_main_layout;
};
@@ -23,6 +23,7 @@
#include "Common/StringUtil.h"

#include "DolphinQt/Config/Mapping/FreeLookGeneral.h"
#include "DolphinQt/Config/Mapping/FreeLookRotation.h"
#include "DolphinQt/Config/Mapping/GCKeyboardEmu.h"
#include "DolphinQt/Config/Mapping/GCMicrophone.h"
#include "DolphinQt/Config/Mapping/GCPadEmu.h"
@@ -436,6 +437,7 @@ void MappingWindow::SetMappingType(MappingWindow::Type type)
{
widget = new FreeLookGeneral(this);
AddWidget(tr("General"), widget);
AddWidget(tr("Rotation"), new FreeLookRotation(this));
setWindowTitle(tr("Free Look Controller %1").arg(GetPort() + 1));
}
break;
@@ -80,6 +80,7 @@
<ClCompile Include="Config\LogConfigWidget.cpp" />
<ClCompile Include="Config\LogWidget.cpp" />
<ClCompile Include="Config\Mapping\FreeLookGeneral.cpp" />
<ClCompile Include="Config\Mapping\FreeLookRotation.cpp" />
<ClCompile Include="Config\Mapping\GCKeyboardEmu.cpp" />
<ClCompile Include="Config\Mapping\GCMicrophone.cpp" />
<ClCompile Include="Config\Mapping\GCPadEmu.cpp" />
@@ -254,6 +255,7 @@
<QtMoc Include="Config\LogConfigWidget.h" />
<QtMoc Include="Config\LogWidget.h" />
<QtMoc Include="Config\Mapping\FreeLookGeneral.h" />
<QtMoc Include="Config\Mapping\FreeLookRotation.h" />
<QtMoc Include="Config\Mapping\GCKeyboardEmu.h" />
<QtMoc Include="Config\Mapping\GCMicrophone.h" />
<QtMoc Include="Config\Mapping\GCPadEmu.h" />

0 comments on commit ce8e87c

Please sign in to comment.