1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-22 21:40:42 +00:00

Implement gyro camera for controllers

This commit is contained in:
uramer 2022-01-18 22:47:49 +01:00
parent 9fa0faf944
commit a496f16cdb
9 changed files with 203 additions and 105 deletions

View File

@ -27,7 +27,7 @@ add_openmw_dir (mwrender
add_openmw_dir (mwinput add_openmw_dir (mwinput
actions actionmanager bindingsmanager controllermanager controlswitch actions actionmanager bindingsmanager controllermanager controlswitch
inputmanagerimp mousemanager keyboardmanager sensormanager inputmanagerimp mousemanager keyboardmanager gyroaxis sensormanager gyromanager
) )
add_openmw_dir (mwgui add_openmw_dir (mwgui

View File

@ -0,0 +1,22 @@
#include "gyroaxis.hpp"
namespace MWInput
{
GyroscopeAxis gyroscopeAxisFromString(std::string_view s)
{
if (s == "x")
return GyroscopeAxis::X;
else if (s == "y")
return GyroscopeAxis::Y;
else if (s == "z")
return GyroscopeAxis::Z;
else if (s == "-x")
return GyroscopeAxis::Minus_X;
else if (s == "-y")
return GyroscopeAxis::Minus_Y;
else if (s == "-z")
return GyroscopeAxis::Minus_Z;
return GyroscopeAxis::Unknown;
}
}

View File

@ -0,0 +1,22 @@
#ifndef MWINPUT_GYROAXIS
#define MWINPUT_GYROAXIS
#include <string_view>
namespace MWInput
{
enum GyroscopeAxis
{
Unknown = 0,
X = 1,
Y = 2,
Z = 3,
Minus_X = -1,
Minus_Y = -2,
Minus_Z = -3
};
GyroscopeAxis gyroscopeAxisFromString(std::string_view s);
}
#endif // !MWINPUT_GYROAXIS

View File

@ -0,0 +1,95 @@
#include "gyromanager.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
namespace MWInput
{
GyroManager::GyroManager()
: mEnabled(Settings::Manager::getBool("enable gyroscope", "Input"))
, mGuiCursorEnabled(true)
, mSensitivityH(Settings::Manager::getFloat("gyro horizontal sensitivity", "Input"))
, mSensitivityV(Settings::Manager::getFloat("gyro vertical sensitivity", "Input"))
, mInvertH(Settings::Manager::getBool("invert x axis", "Input"))
, mInvertV(Settings::Manager::getBool("invert y axis", "Input"))
, mInputThreshold(Settings::Manager::getFloat("gyro input threshold", "Input"))
, mAxisH(gyroscopeAxisFromString(Settings::Manager::getString("gyro horizontal axis", "Input")))
, mAxisV(gyroscopeAxisFromString(Settings::Manager::getString("gyro vertical axis", "Input")))
{};
void GyroManager::update(float dt, std::array<float, 3> values) const
{
if (!mGuiCursorEnabled)
{
float gyroH = getAxisValue(mAxisH, values);
float gyroV = getAxisValue(mAxisV, values);
if (gyroH == 0 && gyroV == 0)
return;
float rot[3];
rot[0] = -gyroV * dt * mSensitivityV * 4 * (mInvertV ? -1 : 1);
rot[1] = 0.0f;
rot[2] = -gyroH * dt * mSensitivityH * 4 * (mInvertH ? -1 : 1);
// Only actually turn player when we're not in vanity mode
bool playerLooking = MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking");
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && playerLooking)
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.yaw(-rot[2]);
player.pitch(-rot[0]);
}
else if (!playerLooking)
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
MWBase::Environment::get().getInputManager()->resetIdleTime();
}
}
void GyroManager::processChangedSettings(const Settings::CategorySettingVector& changed)
{
for (const auto& setting : changed)
{
if (setting.first != "Input")
continue;
if (setting.second == "enable gyroscope")
mEnabled = Settings::Manager::getBool("enable gyroscope", "Input");
else if (setting.second == "gyro horizontal sensitivity")
mSensitivityH = Settings::Manager::getFloat("gyro horizontal sensitivity", "Input");
else if (setting.second == "gyro vertical sensitivity")
mSensitivityV = Settings::Manager::getFloat("gyro vertical sensitivity", "Input");
else if (setting.second == "invert x axis")
mInvertH = Settings::Manager::getBool("invert x axis", "Input");
else if (setting.second == "invert y axis")
mInvertV = Settings::Manager::getBool("invert y axis", "Input");
else if (setting.second == "gyro input threshold")
mInputThreshold = Settings::Manager::getFloat("gyro input threshold", "Input");
else if (setting.second == "gyro horizontal axis")
mAxisH = gyroscopeAxisFromString(Settings::Manager::getString("gyro horizontal axis", "Input"));
else if (setting.second == "gyro vertical axis")
mAxisV = gyroscopeAxisFromString(Settings::Manager::getString("gyro vertical axis", "Input"));
}
}
namespace
{
int signum(int x)
{
return 0 < x - x < 0;
}
}
float GyroManager::getAxisValue(GyroscopeAxis axis, std::array<float, 3> values) const
{
if (axis == GyroscopeAxis::Unknown)
return 0;
float value = values[std::abs(axis) - 1] * signum(axis);
//if (std::abs(value) <= mInputThreshold)
// value = 0;
return value;
}
}

View File

@ -0,0 +1,38 @@
#ifndef MWINPUT_GYROMANAGER
#define MWINPUT_GYROMANAGER
#include <components/settings/settings.hpp>
#include "gyroaxis.hpp"
namespace MWInput
{
class GyroManager
{
public:
GyroManager();
bool isEnabled() const { return mEnabled; }
void update(float dt, std::array<float, 3> values) const;
void processChangedSettings(const Settings::CategorySettingVector& changed);
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
private:
bool mEnabled;
bool mGuiCursorEnabled;
float mSensitivityH;
float mSensitivityV;
bool mInvertH;
bool mInvertV;
float mInputThreshold;
GyroscopeAxis mAxisH;
GyroscopeAxis mAxisV;
float getAxisValue(GyroscopeAxis axis, std::array<float, 3> values) const;
};
}
#endif // !MWINPUT_GYROMANAGER

View File

@ -19,6 +19,7 @@
#include "keyboardmanager.hpp" #include "keyboardmanager.hpp"
#include "mousemanager.hpp" #include "mousemanager.hpp"
#include "sensormanager.hpp" #include "sensormanager.hpp"
#include "gyromanager.hpp"
namespace MWInput namespace MWInput
{ {
@ -51,6 +52,8 @@ namespace MWInput
mSensorManager = new SensorManager(); mSensorManager = new SensorManager();
mInputWrapper->setSensorEventCallback(mSensorManager); mInputWrapper->setSensorEventCallback(mSensorManager);
mGyroManager = new GyroManager();
} }
void InputManager::clear() void InputManager::clear()
@ -72,6 +75,8 @@ namespace MWInput
delete mBindingsManager; delete mBindingsManager;
delete mInputWrapper; delete mInputWrapper;
delete mGyroManager;
} }
void InputManager::setAttemptJump(bool jumping) void InputManager::setAttemptJump(bool jumping)
@ -100,6 +105,17 @@ namespace MWInput
mMouseManager->update(dt); mMouseManager->update(dt);
mSensorManager->update(dt); mSensorManager->update(dt);
mActionManager->update(dt, controllerMove); mActionManager->update(dt, controllerMove);
if (mGyroManager->isEnabled())
{
bool controllerAvailable = mControllerManager->isGyroAvailable();
bool sensorAvailable = mSensorManager->isGyroAvailable();
if (controllerAvailable || sensorAvailable)
{
mGyroManager->update(dt,
controllerAvailable ? mControllerManager->getGyroValues() : mSensorManager->getGyroValues());
}
}
} }
void InputManager::setDragDrop(bool dragDrop) void InputManager::setDragDrop(bool dragDrop)
@ -117,6 +133,7 @@ namespace MWInput
mControllerManager->setGuiCursorEnabled(guiMode); mControllerManager->setGuiCursorEnabled(guiMode);
mMouseManager->setGuiCursorEnabled(guiMode); mMouseManager->setGuiCursorEnabled(guiMode);
mSensorManager->setGuiCursorEnabled(guiMode); mSensorManager->setGuiCursorEnabled(guiMode);
mGyroManager->setGuiCursorEnabled(guiMode);
mMouseManager->setMouseLookEnabled(!guiMode); mMouseManager->setMouseLookEnabled(!guiMode);
if (guiMode) if (guiMode)
MWBase::Environment::get().getWindowManager()->showCrosshair(false); MWBase::Environment::get().getWindowManager()->showCrosshair(false);
@ -130,6 +147,7 @@ namespace MWInput
{ {
mMouseManager->processChangedSettings(changed); mMouseManager->processChangedSettings(changed);
mSensorManager->processChangedSettings(changed); mSensorManager->processChangedSettings(changed);
mGyroManager->processChangedSettings(changed);
} }
bool InputManager::getControlSwitch(std::string_view sw) bool InputManager::getControlSwitch(std::string_view sw)

View File

@ -39,6 +39,7 @@ namespace MWInput
class KeyboardManager; class KeyboardManager;
class MouseManager; class MouseManager;
class SensorManager; class SensorManager;
class GyroManager;
/** /**
* @brief Class that provides a high-level API for game input * @brief Class that provides a high-level API for game input
@ -128,6 +129,7 @@ namespace MWInput
KeyboardManager* mKeyboardManager; KeyboardManager* mKeyboardManager;
MouseManager* mMouseManager; MouseManager* mMouseManager;
SensorManager* mSensorManager; SensorManager* mSensorManager;
GyroManager* mGyroManager;
}; };
} }
#endif #endif

View File

@ -11,15 +11,10 @@
namespace MWInput namespace MWInput
{ {
SensorManager::SensorManager() SensorManager::SensorManager()
: mInvertX(Settings::Manager::getBool("invert x axis", "Input")) : mGyroValues()
, mInvertY(Settings::Manager::getBool("invert y axis", "Input"))
, mGyroValues()
, mGyroUpdateTimer(0.f) , mGyroUpdateTimer(0.f)
, mGyroHSensitivity(Settings::Manager::getFloat("gyro horizontal sensitivity", "Input"))
, mGyroVSensitivity(Settings::Manager::getFloat("gyro vertical sensitivity", "Input"))
, mGyroHAxis(GyroscopeAxis::Minus_X) , mGyroHAxis(GyroscopeAxis::Minus_X)
, mGyroVAxis(GyroscopeAxis::Y) , mGyroVAxis(GyroscopeAxis::Y)
, mGyroInputThreshold(Settings::Manager::getFloat("gyro input threshold", "Input"))
, mGyroscope(nullptr) , mGyroscope(nullptr)
, mGuiCursorEnabled(true) , mGuiCursorEnabled(true)
{ {
@ -41,24 +36,6 @@ namespace MWInput
} }
} }
SensorManager::GyroscopeAxis SensorManager::mapGyroscopeAxis(const std::string& axis)
{
if (axis == "x")
return GyroscopeAxis::X;
else if (axis == "y")
return GyroscopeAxis::Y;
else if (axis == "z")
return GyroscopeAxis::Z;
else if (axis == "-x")
return GyroscopeAxis::Minus_X;
else if (axis == "-y")
return GyroscopeAxis::Minus_Y;
else if (axis == "-z")
return GyroscopeAxis::Minus_Z;
return GyroscopeAxis::Unknown;
}
void SensorManager::correctGyroscopeAxes() void SensorManager::correctGyroscopeAxes()
{ {
if (!Settings::Manager::getBool("enable gyroscope", "Input")) if (!Settings::Manager::getBool("enable gyroscope", "Input"))
@ -67,8 +44,8 @@ namespace MWInput
// Treat setting from config as axes for landscape mode. // Treat setting from config as axes for landscape mode.
// If the device does not support orientation change, do nothing. // If the device does not support orientation change, do nothing.
// Note: in is unclear how to correct axes for devices with non-standart Z axis direction. // Note: in is unclear how to correct axes for devices with non-standart Z axis direction.
mGyroHAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro horizontal axis", "Input")); mGyroHAxis = gyroscopeAxisFromString(Settings::Manager::getString("gyro horizontal axis", "Input"));
mGyroVAxis = mapGyroscopeAxis(Settings::Manager::getString("gyro vertical axis", "Input")); mGyroVAxis = gyroscopeAxisFromString(Settings::Manager::getString("gyro vertical axis", "Input"));
SDL_DisplayOrientation currentOrientation = SDL_GetDisplayOrientation(Settings::Manager::getInt("screen", "Video")); SDL_DisplayOrientation currentOrientation = SDL_GetDisplayOrientation(Settings::Manager::getInt("screen", "Video"));
switch (currentOrientation) switch (currentOrientation)
@ -148,18 +125,6 @@ namespace MWInput
{ {
for (const auto& setting : changed) for (const auto& setting : changed)
{ {
if (setting.first == "Input" && setting.second == "invert x axis")
mInvertX = Settings::Manager::getBool("invert x axis", "Input");
if (setting.first == "Input" && setting.second == "invert y axis")
mInvertY = Settings::Manager::getBool("invert y axis", "Input");
if (setting.first == "Input" && setting.second == "gyro horizontal sensitivity")
mGyroHSensitivity = Settings::Manager::getFloat("gyro horizontal sensitivity", "Input");
if (setting.first == "Input" && setting.second == "gyro vertical sensitivity")
mGyroVSensitivity = Settings::Manager::getFloat("gyro vertical sensitivity", "Input");
if (setting.first == "Input" && setting.second == "enable gyroscope") if (setting.first == "Input" && setting.second == "enable gyroscope")
init(); init();
@ -168,26 +133,6 @@ namespace MWInput
if (setting.first == "Input" && setting.second == "gyro vertical axis") if (setting.first == "Input" && setting.second == "gyro vertical axis")
correctGyroscopeAxes(); correctGyroscopeAxes();
if (setting.first == "Input" && setting.second == "gyro input threshold")
mGyroInputThreshold = Settings::Manager::getFloat("gyro input threshold", "Input");
}
}
float SensorManager::getGyroAxisSpeed(GyroscopeAxis axis) const
{
switch (axis)
{
case GyroscopeAxis::X:
case GyroscopeAxis::Y:
case GyroscopeAxis::Z:
return std::abs(mGyroValues[0]) >= mGyroInputThreshold ? mGyroValues[axis - 1] : 0.f;
case GyroscopeAxis::Minus_X:
case GyroscopeAxis::Minus_Y:
case GyroscopeAxis::Minus_Z:
return std::abs(mGyroValues[0]) >= mGyroInputThreshold ? -mGyroValues[std::abs(axis) - 1] : 0.f;
default:
return 0.f;
} }
} }
@ -236,34 +181,6 @@ namespace MWInput
// Reset current rotation speed and wait for update. // Reset current rotation speed and wait for update.
mGyroValues = { 0, 0, 0 }; mGyroValues = { 0, 0, 0 };
mGyroUpdateTimer = 0.f; mGyroUpdateTimer = 0.f;
return;
}
if (!mGuiCursorEnabled)
{
float gyroH = getGyroAxisSpeed(mGyroHAxis);
float gyroV = getGyroAxisSpeed(mGyroVAxis);
if (gyroH == 0 && gyroV == 0)
return;
float rot[3];
rot[0] = -gyroV * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1);
rot[1] = 0.0f;
rot[2] = -gyroH * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1);
// Only actually turn player when we're not in vanity mode
bool playerLooking = MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking");
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && playerLooking)
{
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.yaw(-rot[2]);
player.pitch(-rot[0]);
}
else if (!playerLooking)
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
MWBase::Environment::get().getInputManager()->resetIdleTime();
} }
} }

View File

@ -6,6 +6,8 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/sdlutil/events.hpp> #include <components/sdlutil/events.hpp>
#include "gyroaxis.hpp"
namespace SDLUtil namespace SDLUtil
{ {
class InputWrapper; class InputWrapper;
@ -39,33 +41,15 @@ namespace MWInput
std::array<float, 3> getGyroValues() const; std::array<float, 3> getGyroValues() const;
private: private:
enum GyroscopeAxis
{
Unknown = 0,
X = 1,
Y = 2,
Z = 3,
Minus_X = -1,
Minus_Y = -2,
Minus_Z = -3
};
void updateSensors(); void updateSensors();
void correctGyroscopeAxes(); void correctGyroscopeAxes();
GyroscopeAxis mapGyroscopeAxis(const std::string& axis);
float getGyroAxisSpeed(GyroscopeAxis axis) const;
bool mInvertX;
bool mInvertY;
std::array<float, 3> mGyroValues; std::array<float, 3> mGyroValues;
float mGyroUpdateTimer; float mGyroUpdateTimer;
float mGyroHSensitivity;
float mGyroVSensitivity;
GyroscopeAxis mGyroHAxis; GyroscopeAxis mGyroHAxis;
GyroscopeAxis mGyroVAxis; GyroscopeAxis mGyroVAxis;
float mGyroInputThreshold;
SDL_Sensor* mGyroscope; SDL_Sensor* mGyroscope;