mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-19 11:29:08 -06:00
Add support for several joysticks at the smae time
This commit is contained in:
@@ -77,43 +77,55 @@ public:
|
||||
void updateStateFromInput(
|
||||
const JoystickInputStates& joystickInputStates, double deltaTime);
|
||||
|
||||
void setAxisMapping(int axis, AxisType mapping,
|
||||
void setAxisMapping(const std::string& joystickName, int axis, AxisType mapping,
|
||||
AxisInvert shouldInvert = AxisInvert::No,
|
||||
AxisNormalize shouldNormalize = AxisNormalize::No,
|
||||
bool isSticky = false, double sensitivity = 0.0
|
||||
);
|
||||
|
||||
AxisInformation axisMapping(int axis) const;
|
||||
AxisInformation axisMapping(const std::string& joystickName, int axis) const;
|
||||
|
||||
void setDeadzone(int axis, float deadzone);
|
||||
float deadzone(int axis) const;
|
||||
void setDeadzone(const std::string& joystickName, int axis, float deadzone);
|
||||
float deadzone(const std::string& joystickName, int axis) const;
|
||||
|
||||
|
||||
void bindButtonCommand(int button, std::string command, JoystickAction action,
|
||||
ButtonCommandRemote remote, std::string documentation);
|
||||
void clearButtonCommand(int button);
|
||||
std::vector<std::string> buttonCommand(int button) const;
|
||||
void bindButtonCommand(const std::string& joystickName, int button,
|
||||
std::string command, JoystickAction action, ButtonCommandRemote remote,
|
||||
std::string documentation);
|
||||
void clearButtonCommand(const std::string& joystickName, int button);
|
||||
std::vector<std::string> buttonCommand(const std::string& joystickName,
|
||||
int button) const;
|
||||
|
||||
private:
|
||||
// We use an array for the axes and a map for the buttons since the axis are going to
|
||||
// be accessed much more often and thus have to be more efficient. And storing a few
|
||||
// extra AxisInformation that are not used will not matter that much; finding an axis
|
||||
// location in a potential map each frame, however, would
|
||||
struct JoystickCameraState {
|
||||
std::string joystickName;
|
||||
|
||||
std::array<AxisInformation, JoystickInputState::MaxAxes> _axisMapping;
|
||||
// We use an array for the axes and a map for the buttons since the axis are going to
|
||||
// be accessed much more often and thus have to be more efficient. And storing a few
|
||||
// extra AxisInformation that are not used will not matter that much; finding an axis
|
||||
// location in a potential map each frame, however, would
|
||||
|
||||
// This array is used to store the old axis values from the previous frame,
|
||||
// it is used to calculate the difference in the values in the case of a sticky axis
|
||||
std::array<float, JoystickInputState::MaxAxes> _prevAxisValues;
|
||||
std::array<AxisInformation, JoystickInputState::MaxAxes> axisMapping;
|
||||
|
||||
struct ButtonInformation {
|
||||
std::string command;
|
||||
JoystickAction action;
|
||||
ButtonCommandRemote synchronization;
|
||||
std::string documentation;
|
||||
// This array is used to store the old axis values from the previous frame,
|
||||
// it is used to calculate the difference in the values in the case of a sticky axis
|
||||
std::array<float, JoystickInputState::MaxAxes> prevAxisValues;
|
||||
|
||||
struct ButtonInformation {
|
||||
std::string command;
|
||||
JoystickAction action;
|
||||
ButtonCommandRemote synchronization;
|
||||
std::string documentation;
|
||||
};
|
||||
|
||||
std::multimap<int, ButtonInformation> buttonMapping;
|
||||
};
|
||||
|
||||
std::multimap<int, ButtonInformation> _buttonMapping;
|
||||
std::vector<JoystickCameraState> _joystickCameraStates;
|
||||
|
||||
// Find the item in _joystickCameraStates that corresponds to the given joystickName
|
||||
// return a pointer to the item, if not found then return nullptr
|
||||
JoystickCameraState* getJoystickCameraState(const std::string& joystickName);
|
||||
const JoystickCameraState* getJoystickCameraState(const std::string& joystickName) const;
|
||||
};
|
||||
|
||||
} // namespace openspace::interaction
|
||||
|
||||
@@ -72,11 +72,6 @@ struct JoystickInputState {
|
||||
/// \c nAxes values are defined values, the rest are undefined
|
||||
std::array<float, MaxAxes> axes;
|
||||
|
||||
/// The axis values can either go back to 0 when the joystick is released or it can
|
||||
/// stay at the value it was before the joystick was released.
|
||||
/// The latter is called a sticky axis, when the values don't go back to 0.
|
||||
bool isSticky = false;
|
||||
|
||||
/// The number of buttons that this joystick possesses
|
||||
int nButtons = 0;
|
||||
/// The status of each button. Only the first \c nButtons values are defined, the rest
|
||||
@@ -88,6 +83,10 @@ struct JoystickInputState {
|
||||
/// derived from the available GLFW constants
|
||||
constexpr const int MaxJoysticks = 16;
|
||||
struct JoystickInputStates : public std::array<JoystickInputState, MaxJoysticks> {
|
||||
/// The maximum number of joysticks that are supported by this system. This number is
|
||||
/// derived from the available GLFW constants
|
||||
static constexpr const int MaxNumJoysticks = 16;
|
||||
|
||||
/**
|
||||
* This function adds the contributions of all connected joysticks for the provided
|
||||
* \p axis. After adding each joysticks contribution, the result is clamped to [-1,1].
|
||||
@@ -99,7 +98,7 @@ struct JoystickInputStates : public std::array<JoystickInputState, MaxJoysticks>
|
||||
*
|
||||
* \pre \p axis must be 0 or positive
|
||||
*/
|
||||
float axis(int axis) const;
|
||||
float axis(const std::string& joystickName, int axis) const;
|
||||
|
||||
/**
|
||||
* This functions checks whether any connected joystick has its \p button in the
|
||||
@@ -113,7 +112,7 @@ struct JoystickInputStates : public std::array<JoystickInputState, MaxJoysticks>
|
||||
*
|
||||
* \pre \p button must be 0 or positive
|
||||
*/
|
||||
bool button(int button, JoystickAction action) const;
|
||||
bool button(const std::string& joystickName, int button, JoystickAction action) const;
|
||||
};
|
||||
|
||||
} // namespace openspace::interaction
|
||||
|
||||
@@ -98,7 +98,8 @@ public:
|
||||
void mousePositionCallback(double x, double y);
|
||||
void mouseScrollWheelCallback(double pos);
|
||||
|
||||
void setJoystickAxisMapping(int axis, JoystickCameraStates::AxisType mapping,
|
||||
void setJoystickAxisMapping(const std::string& joystickName,
|
||||
int axis, JoystickCameraStates::AxisType mapping,
|
||||
JoystickCameraStates::AxisInvert shouldInvert =
|
||||
JoystickCameraStates::AxisInvert::No,
|
||||
JoystickCameraStates::AxisNormalize shouldNormalize =
|
||||
@@ -106,16 +107,20 @@ public:
|
||||
bool isSticky = false, double sensitivity = 0.0
|
||||
);
|
||||
|
||||
JoystickCameraStates::AxisInformation joystickAxisMapping(int axis) const;
|
||||
JoystickCameraStates::AxisInformation joystickAxisMapping(
|
||||
const std::string& joystickName, int axis) const;
|
||||
|
||||
void setJoystickAxisDeadzone(int axis, float deadzone);
|
||||
float joystickAxisDeadzone(int axis) const;
|
||||
void setJoystickAxisDeadzone(const std::string& joystickName, int axis,
|
||||
float deadzone);
|
||||
float joystickAxisDeadzone(const std::string& joystickName, int axis) const;
|
||||
|
||||
void bindJoystickButtonCommand(int button, std::string command, JoystickAction action,
|
||||
void bindJoystickButtonCommand(const std::string& joystickName, int button,
|
||||
std::string command, JoystickAction action,
|
||||
JoystickCameraStates::ButtonCommandRemote remote, std::string documentation);
|
||||
|
||||
void clearJoystickButtonCommand(int button);
|
||||
std::vector<std::string> joystickButtonCommand(int button) const;
|
||||
void clearJoystickButtonCommand(const std::string& joystickName, int button);
|
||||
std::vector<std::string> joystickButtonCommand(const std::string& joystickName,
|
||||
int button) const;
|
||||
|
||||
// Websockets
|
||||
void setWebsocketAxisMapping(int axis, WebsocketCameraStates::AxisType mapping,
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include <modules/imgui/include/imgui_include.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/interaction/inputstate.h>
|
||||
#include <openspace/interaction/joystickinputstate.h>
|
||||
|
||||
namespace {
|
||||
@@ -86,7 +85,7 @@ void GuiJoystickComponent::render() {
|
||||
ImGui::Text("%s", "Summed contributions");
|
||||
ImGui::Text("%s", "Axes");
|
||||
for (int i = 0; i < JoystickInputState::MaxAxes; ++i) {
|
||||
float f = global::joystickInputStates->axis(i);
|
||||
float f = global::joystickInputStates->axis("", i);
|
||||
ImGui::SliderFloat(
|
||||
std::to_string(i).c_str(),
|
||||
&f,
|
||||
@@ -98,8 +97,8 @@ void GuiJoystickComponent::render() {
|
||||
for (int i = 0; i < JoystickInputState::MaxButtons; ++i) {
|
||||
ImGui::RadioButton(
|
||||
std::to_string(i).c_str(),
|
||||
global::joystickInputStates->button(i, JoystickAction::Press) ||
|
||||
global::joystickInputStates->button(i, JoystickAction::Repeat)
|
||||
global::joystickInputStates->button("", i, JoystickAction::Press) ||
|
||||
global::joystickInputStates->button("", i, JoystickAction::Repeat)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,15 @@
|
||||
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/exception.h>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "JoystickCameraStates";
|
||||
} // namespace
|
||||
|
||||
namespace openspace::interaction {
|
||||
|
||||
JoystickCameraStates::JoystickCameraStates(double sensitivity, double velocityScaleFactor)
|
||||
@@ -46,49 +51,61 @@ void JoystickCameraStates::updateStateFromInput(
|
||||
std::pair<bool, glm::dvec2> globalRoll = { false, glm::dvec2(0.0) };
|
||||
std::pair<bool, glm::dvec2> localRotation = { false, glm::dvec2(0.0) };
|
||||
|
||||
for (int i = 0; i < JoystickInputState::MaxAxes; ++i) {
|
||||
AxisInformation t = _axisMapping[i];
|
||||
if (t.type == AxisType::None) {
|
||||
for (const JoystickInputState& joystickInputState : joystickInputStates) {
|
||||
if (joystickInputState.name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float rawValue = joystickInputStates.axis(i);
|
||||
float value = rawValue;
|
||||
JoystickCameraState* joystickCameraState =
|
||||
getJoystickCameraState(joystickInputState.name);
|
||||
|
||||
if (t.isSticky) {
|
||||
value = rawValue - _prevAxisValues[i];
|
||||
_prevAxisValues[i] = rawValue;
|
||||
}
|
||||
|
||||
if (std::fabs(value) <= t.deadzone) {
|
||||
if (!joystickCameraState) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.normalize) {
|
||||
value = (value + 1.f) / 2.f;
|
||||
}
|
||||
for (int i = 0; i < JoystickInputState::MaxAxes; ++i) {
|
||||
AxisInformation t = joystickCameraState->axisMapping[i];
|
||||
if (t.type == AxisType::None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.invert) {
|
||||
value *= -1.f;
|
||||
}
|
||||
float rawValue = joystickInputStates.axis(joystickInputState.name, i);
|
||||
float value = rawValue;
|
||||
|
||||
if (std::abs(t.sensitivity) > std::numeric_limits<double>::epsilon()) {
|
||||
value = static_cast<float>(value * t.sensitivity * _sensitivity);
|
||||
}
|
||||
else {
|
||||
value = static_cast<float>(value * _sensitivity);
|
||||
}
|
||||
if (t.isSticky) {
|
||||
value = rawValue - joystickCameraState->prevAxisValues[i];
|
||||
joystickCameraState->prevAxisValues[i] = rawValue;
|
||||
}
|
||||
|
||||
switch (t.type) {
|
||||
if (std::fabs(value) <= t.deadzone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.normalize) {
|
||||
value = (value + 1.f) / 2.f;
|
||||
}
|
||||
|
||||
if (t.invert) {
|
||||
value *= -1.f;
|
||||
}
|
||||
|
||||
if (std::abs(t.sensitivity) > std::numeric_limits<double>::epsilon()) {
|
||||
value = static_cast<float>(value * t.sensitivity * _sensitivity);
|
||||
}
|
||||
else {
|
||||
value = static_cast<float>(value * _sensitivity);
|
||||
}
|
||||
|
||||
switch (t.type) {
|
||||
case AxisType::None:
|
||||
break;
|
||||
case AxisType::OrbitX:
|
||||
globalRotation.first = true;
|
||||
globalRotation.second.x = value;
|
||||
globalRotation.second.x += value;
|
||||
break;
|
||||
case AxisType::OrbitY:
|
||||
globalRotation.first = true;
|
||||
globalRotation.second.y = value;
|
||||
globalRotation.second.y += value;
|
||||
break;
|
||||
case AxisType::Zoom:
|
||||
case AxisType::ZoomIn:
|
||||
@@ -101,28 +118,47 @@ void JoystickCameraStates::updateStateFromInput(
|
||||
break;
|
||||
case AxisType::LocalRollX:
|
||||
localRoll.first = true;
|
||||
localRoll.second.x = value;
|
||||
localRoll.second.x += value;
|
||||
break;
|
||||
case AxisType::LocalRollY:
|
||||
localRoll.first = true;
|
||||
localRoll.second.y = value;
|
||||
localRoll.second.y += value;
|
||||
break;
|
||||
case AxisType::GlobalRollX:
|
||||
globalRoll.first = true;
|
||||
globalRoll.second.x = value;
|
||||
globalRoll.second.x += value;
|
||||
break;
|
||||
case AxisType::GlobalRollY:
|
||||
globalRoll.first = true;
|
||||
globalRoll.second.y = value;
|
||||
globalRoll.second.y += value;
|
||||
break;
|
||||
case AxisType::PanX:
|
||||
localRotation.first = true;
|
||||
localRotation.second.x = value;
|
||||
localRotation.second.x += value;
|
||||
break;
|
||||
case AxisType::PanY:
|
||||
localRotation.first = true;
|
||||
localRotation.second.y = value;
|
||||
localRotation.second.y += value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < JoystickInputState::MaxButtons; ++i) {
|
||||
auto itRange = joystickCameraState->buttonMapping.equal_range(i);
|
||||
for (auto it = itRange.first; it != itRange.second; ++it) {
|
||||
bool active =global::joystickInputStates->button(
|
||||
joystickInputState.name,
|
||||
i,
|
||||
it->second.action
|
||||
);
|
||||
|
||||
if (active) {
|
||||
global::scriptEngine->queueScript(
|
||||
it->second.command,
|
||||
scripting::ScriptEngine::RemoteScripting(it->second.synchronization)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,23 +196,10 @@ void JoystickCameraStates::updateStateFromInput(
|
||||
else {
|
||||
_localRotationState.velocity.decelerate(deltaTime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < JoystickInputState::MaxButtons; ++i) {
|
||||
auto itRange = _buttonMapping.equal_range(i);
|
||||
for (auto it = itRange.first; it != itRange.second; ++it) {
|
||||
bool active = global::joystickInputStates->button(i, it->second.action);
|
||||
|
||||
if (active) {
|
||||
global::scriptEngine->queueScript(
|
||||
it->second.command,
|
||||
scripting::ScriptEngine::RemoteScripting(it->second.synchronization)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JoystickCameraStates::setAxisMapping(int axis, AxisType mapping,
|
||||
void JoystickCameraStates::setAxisMapping(const std::string& joystickName,
|
||||
int axis, AxisType mapping,
|
||||
AxisInvert shouldInvert,
|
||||
AxisNormalize shouldNormalize,
|
||||
bool isSticky,
|
||||
@@ -184,48 +207,119 @@ void JoystickCameraStates::setAxisMapping(int axis, AxisType mapping,
|
||||
{
|
||||
ghoul_assert(axis < JoystickInputState::MaxAxes, "axis must be < MaxAxes");
|
||||
|
||||
_axisMapping[axis].type = mapping;
|
||||
_axisMapping[axis].invert = shouldInvert;
|
||||
_axisMapping[axis].normalize = shouldNormalize;
|
||||
_axisMapping[axis].isSticky = isSticky;
|
||||
_axisMapping[axis].sensitivity = sensitivity;
|
||||
|
||||
if (isSticky) {
|
||||
global::joystickInputStates->at(axis).isSticky = true;
|
||||
JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName);
|
||||
if (!joystickCameraState) {
|
||||
if (_joystickCameraStates.size() < JoystickInputStates::MaxNumJoysticks) {
|
||||
_joystickCameraStates.push_back(JoystickCameraState());
|
||||
joystickCameraState = &_joystickCameraStates.back();
|
||||
joystickCameraState->joystickName = joystickName;
|
||||
}
|
||||
else {
|
||||
LWARNING(fmt::format("Cannot add more joysticks, only {} joysticks are "
|
||||
"supported", JoystickInputStates::MaxNumJoysticks));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_prevAxisValues[axis] = global::joystickInputStates->axis(axis);
|
||||
joystickCameraState->axisMapping[axis].type = mapping;
|
||||
joystickCameraState->axisMapping[axis].invert = shouldInvert;
|
||||
joystickCameraState->axisMapping[axis].normalize = shouldNormalize;
|
||||
joystickCameraState->axisMapping[axis].isSticky = isSticky;
|
||||
joystickCameraState->axisMapping[axis].sensitivity = sensitivity;
|
||||
|
||||
joystickCameraState->prevAxisValues[axis] =
|
||||
global::joystickInputStates->axis(joystickName, axis);
|
||||
}
|
||||
|
||||
JoystickCameraStates::AxisInformation JoystickCameraStates::axisMapping(int axis) const {
|
||||
return _axisMapping[axis];
|
||||
JoystickCameraStates::AxisInformation JoystickCameraStates::axisMapping(
|
||||
const std::string& joystickName,
|
||||
int axis) const
|
||||
{
|
||||
const JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName);
|
||||
if (!joystickCameraState) {
|
||||
LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}' "
|
||||
"(axisMapping)", joystickName));
|
||||
JoystickCameraStates::AxisInformation dummy;
|
||||
return dummy;
|
||||
}
|
||||
|
||||
return joystickCameraState->axisMapping[axis];
|
||||
}
|
||||
|
||||
void JoystickCameraStates::setDeadzone(int axis, float deadzone) {
|
||||
_axisMapping[axis].deadzone = deadzone;
|
||||
void JoystickCameraStates::setDeadzone(const std::string& joystickName, int axis,
|
||||
float deadzone)
|
||||
{
|
||||
JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName);
|
||||
if (!joystickCameraState) {
|
||||
if (_joystickCameraStates.size() < JoystickInputStates::MaxNumJoysticks) {
|
||||
_joystickCameraStates.push_back(JoystickCameraState());
|
||||
joystickCameraState = &_joystickCameraStates.back();
|
||||
joystickCameraState->joystickName = joystickName;
|
||||
}
|
||||
else {
|
||||
LWARNING(fmt::format("Cannot add more joysticks, only {} joysticks are "
|
||||
"supported", JoystickInputStates::MaxNumJoysticks));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
joystickCameraState->axisMapping[axis].deadzone = deadzone;
|
||||
}
|
||||
|
||||
float JoystickCameraStates::deadzone(int axis) const {
|
||||
return _axisMapping[axis].deadzone;
|
||||
float JoystickCameraStates::deadzone(const std::string& joystickName, int axis) const {
|
||||
const JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName);
|
||||
if (!joystickCameraState) {
|
||||
LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}' (deadzone)",
|
||||
joystickName));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return joystickCameraState->axisMapping[axis].deadzone;
|
||||
}
|
||||
|
||||
void JoystickCameraStates::bindButtonCommand(int button, std::string command,
|
||||
void JoystickCameraStates::bindButtonCommand(const std::string& joystickName,
|
||||
int button, std::string command,
|
||||
JoystickAction action,
|
||||
ButtonCommandRemote remote,
|
||||
std::string documentation)
|
||||
{
|
||||
_buttonMapping.insert({
|
||||
JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName);
|
||||
if (!joystickCameraState) {
|
||||
if (_joystickCameraStates.size() < JoystickInputStates::MaxNumJoysticks) {
|
||||
_joystickCameraStates.push_back(JoystickCameraState());
|
||||
joystickCameraState = &_joystickCameraStates.back();
|
||||
joystickCameraState->joystickName = joystickName;
|
||||
}
|
||||
else {
|
||||
LWARNING(fmt::format("Cannot add more joysticks, only {} joysticks are "
|
||||
"supported", JoystickInputStates::MaxNumJoysticks));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
joystickCameraState->buttonMapping.insert({
|
||||
button,
|
||||
{ std::move(command), action, remote, std::move(documentation) }
|
||||
});
|
||||
}
|
||||
|
||||
void JoystickCameraStates::clearButtonCommand(int button) {
|
||||
for (auto it = _buttonMapping.begin(); it != _buttonMapping.end();) {
|
||||
void JoystickCameraStates::clearButtonCommand(const std::string& joystickName,
|
||||
int button)
|
||||
{
|
||||
JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName);
|
||||
if (!joystickCameraState) {
|
||||
LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}' "
|
||||
"(clearButtonCommand)", joystickName));
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto it = joystickCameraState->buttonMapping.begin();
|
||||
it != joystickCameraState->buttonMapping.end(); )
|
||||
{
|
||||
// If the current iterator is the button that we are looking for, delete it
|
||||
// (std::multimap::erase will return the iterator to the next element for us)
|
||||
if (it->first == button) {
|
||||
it = _buttonMapping.erase(it);
|
||||
it = joystickCameraState->buttonMapping.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
@@ -233,14 +327,48 @@ void JoystickCameraStates::clearButtonCommand(int button) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> JoystickCameraStates::buttonCommand(int button) const {
|
||||
std::vector<std::string> JoystickCameraStates::buttonCommand(
|
||||
const std::string& joystickName,
|
||||
int button) const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
auto itRange = _buttonMapping.equal_range(button);
|
||||
const JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName);
|
||||
if (!joystickCameraState) {
|
||||
LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}' "
|
||||
"(buttonCommand)", joystickName));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto itRange = joystickCameraState->buttonMapping.equal_range(button);
|
||||
for (auto it = itRange.first; it != itRange.second; ++it) {
|
||||
result.push_back(it->second.command);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JoystickCameraStates::JoystickCameraState* JoystickCameraStates::getJoystickCameraState(
|
||||
const std::string& joystickName)
|
||||
{
|
||||
for (JoystickCameraState& joystickCameraState : _joystickCameraStates) {
|
||||
if (joystickCameraState.joystickName == joystickName) {
|
||||
return &joystickCameraState;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const JoystickCameraStates::JoystickCameraState* JoystickCameraStates::getJoystickCameraState(
|
||||
const std::string& joystickName) const
|
||||
{
|
||||
for (const JoystickCameraState& joystickCameraState : _joystickCameraStates) {
|
||||
if (joystickCameraState.joystickName == joystickName) {
|
||||
return &joystickCameraState;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
} // namespace openspace::interaction
|
||||
|
||||
@@ -33,38 +33,68 @@
|
||||
|
||||
namespace openspace::interaction {
|
||||
|
||||
float JoystickInputStates::axis(int axis) const {
|
||||
float JoystickInputStates::axis(const std::string& joystickName, int axis) const {
|
||||
ghoul_precondition(axis >= 0, "axis must be 0 or positive");
|
||||
|
||||
float res = std::accumulate(
|
||||
begin(),
|
||||
end(),
|
||||
0.f,
|
||||
[axis](float value, const JoystickInputState& state) {
|
||||
if (state.isConnected) {
|
||||
value += state.axes[axis];
|
||||
if(joystickName.empty()) {
|
||||
float res = std::accumulate(
|
||||
begin(),
|
||||
end(),
|
||||
0.f,
|
||||
[axis](float value, const JoystickInputState& state) {
|
||||
if (state.isConnected) {
|
||||
value += state.axes[axis];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
// If multiple joysticks are connected, we might get values outside the -1,1 range by
|
||||
// summing them up
|
||||
glm::clamp(res, -1.f, 1.f);
|
||||
return res;
|
||||
// If multiple joysticks are connected, we might get values outside the -1,1 range by
|
||||
// summing them up
|
||||
glm::clamp(res, -1.f, 1.f);
|
||||
return res;
|
||||
}
|
||||
|
||||
const JoystickInputState* state = nullptr;
|
||||
for (auto it = begin(); it < end(); ++it) {
|
||||
if (it->name == joystickName) {
|
||||
state = &(*it);
|
||||
}
|
||||
}
|
||||
|
||||
if (!state) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return state->axes[axis];
|
||||
}
|
||||
|
||||
bool JoystickInputStates::button(int button, JoystickAction action) const {
|
||||
bool JoystickInputStates::button(const std::string& joystickName, int button, JoystickAction action) const {
|
||||
ghoul_precondition(button >= 0, "button must be 0 or positive");
|
||||
|
||||
bool res = std::any_of(
|
||||
begin(),
|
||||
end(),
|
||||
[button, action](const JoystickInputState& state) {
|
||||
return state.isConnected ? (state.buttons[button] == action) : false;
|
||||
if(joystickName.empty()) {
|
||||
bool res = std::any_of(
|
||||
begin(),
|
||||
end(),
|
||||
[button, action](const JoystickInputState& state) {
|
||||
return state.isConnected ? (state.buttons[button] == action) : false;
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
const JoystickInputState* state = nullptr;
|
||||
for (auto it = begin(); it < end(); ++it) {
|
||||
if (it->name == joystickName) {
|
||||
state = &(*it);
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return state->isConnected ? (state->buttons[button] == action) : false;
|
||||
}
|
||||
|
||||
} // namespace openspace::interaction
|
||||
|
||||
@@ -498,7 +498,7 @@ void NavigationHandler::loadNavigationState(const std::string& filepath) {
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationHandler::setJoystickAxisMapping(int axis,
|
||||
void NavigationHandler::setJoystickAxisMapping(const std::string& joystickName, int axis,
|
||||
JoystickCameraStates::AxisType mapping,
|
||||
JoystickCameraStates::AxisInvert shouldInvert,
|
||||
JoystickCameraStates::AxisNormalize shouldNormalize,
|
||||
@@ -506,6 +506,7 @@ void NavigationHandler::setJoystickAxisMapping(int axis,
|
||||
double sensitivity)
|
||||
{
|
||||
_orbitalNavigator.joystickStates().setAxisMapping(
|
||||
joystickName,
|
||||
axis,
|
||||
mapping,
|
||||
shouldInvert,
|
||||
@@ -530,25 +531,31 @@ void NavigationHandler::setWebsocketAxisMapping(int axis,
|
||||
|
||||
|
||||
JoystickCameraStates::AxisInformation
|
||||
NavigationHandler::joystickAxisMapping(int axis) const
|
||||
NavigationHandler::joystickAxisMapping(const std::string& joystickName, int axis) const
|
||||
{
|
||||
return _orbitalNavigator.joystickStates().axisMapping(axis);
|
||||
return _orbitalNavigator.joystickStates().axisMapping(joystickName, axis);
|
||||
}
|
||||
|
||||
void NavigationHandler::setJoystickAxisDeadzone(int axis, float deadzone) {
|
||||
_orbitalNavigator.joystickStates().setDeadzone(axis, deadzone);
|
||||
void NavigationHandler::setJoystickAxisDeadzone(const std::string& joystickName, int axis,
|
||||
float deadzone)
|
||||
{
|
||||
_orbitalNavigator.joystickStates().setDeadzone(joystickName, axis, deadzone);
|
||||
}
|
||||
|
||||
float NavigationHandler::joystickAxisDeadzone(int axis) const {
|
||||
return _orbitalNavigator.joystickStates().deadzone(axis);
|
||||
float NavigationHandler::joystickAxisDeadzone(const std::string& joystickName,
|
||||
int axis) const
|
||||
{
|
||||
return _orbitalNavigator.joystickStates().deadzone(joystickName, axis);
|
||||
}
|
||||
|
||||
void NavigationHandler::bindJoystickButtonCommand(int button, std::string command,
|
||||
void NavigationHandler::bindJoystickButtonCommand(const std::string& joystickName,
|
||||
int button, std::string command,
|
||||
JoystickAction action,
|
||||
JoystickCameraStates::ButtonCommandRemote remote,
|
||||
std::string documentation)
|
||||
{
|
||||
_orbitalNavigator.joystickStates().bindButtonCommand(
|
||||
joystickName,
|
||||
button,
|
||||
std::move(command),
|
||||
action,
|
||||
@@ -557,12 +564,16 @@ void NavigationHandler::bindJoystickButtonCommand(int button, std::string comman
|
||||
);
|
||||
}
|
||||
|
||||
void NavigationHandler::clearJoystickButtonCommand(int button) {
|
||||
_orbitalNavigator.joystickStates().clearButtonCommand(button);
|
||||
void NavigationHandler::clearJoystickButtonCommand(const std::string& joystickName,
|
||||
int button)
|
||||
{
|
||||
_orbitalNavigator.joystickStates().clearButtonCommand(joystickName, button);
|
||||
}
|
||||
|
||||
std::vector<std::string> NavigationHandler::joystickButtonCommand(int button) const {
|
||||
return _orbitalNavigator.joystickStates().buttonCommand(button);
|
||||
std::vector<std::string> NavigationHandler::joystickButtonCommand(
|
||||
const std::string& joystickName, int button) const
|
||||
{
|
||||
return _orbitalNavigator.joystickStates().buttonCommand(joystickName, button);
|
||||
}
|
||||
|
||||
scripting::LuaLibrary NavigationHandler::luaLibrary() {
|
||||
|
||||
@@ -151,10 +151,11 @@ int retargetAim(lua_State* L) {
|
||||
}
|
||||
|
||||
int bindJoystickAxis(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, { 2, 6 }, "lua::bindJoystickAxis");
|
||||
auto [axis, axisType, shouldInvert, shouldNormalize, isSticky, sensitivity] =
|
||||
ghoul::lua::checkArgumentsAndThrow(L, { 3, 7 }, "lua::bindJoystickAxis");
|
||||
auto [joystickName, axis, axisType, shouldInvert, shouldNormalize, isSticky,
|
||||
sensitivity] =
|
||||
ghoul::lua::values<
|
||||
int, std::string, std::optional<bool>, std::optional<bool>,
|
||||
std::string, int, std::string, std::optional<bool>, std::optional<bool>,
|
||||
std::optional<bool>, std::optional<double>
|
||||
>(L);
|
||||
shouldInvert = shouldInvert.value_or(false);
|
||||
@@ -163,6 +164,7 @@ int bindJoystickAxis(lua_State* L) {
|
||||
sensitivity = sensitivity.value_or(0.0);
|
||||
|
||||
global::navigationHandler->setJoystickAxisMapping(
|
||||
joystickName,
|
||||
axis,
|
||||
ghoul::from_string<interaction::JoystickCameraStates::AxisType>(axisType),
|
||||
interaction::JoystickCameraStates::AxisInvert(*shouldInvert),
|
||||
@@ -174,11 +176,11 @@ int bindJoystickAxis(lua_State* L) {
|
||||
}
|
||||
|
||||
int joystickAxis(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::joystickAxis");
|
||||
const int axis = ghoul::lua::value<int>(L);
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::joystickAxis");
|
||||
auto [joystickName, axis] = ghoul::lua::values<std::string, int>(L);
|
||||
|
||||
using AI = interaction::JoystickCameraStates::AxisInformation;
|
||||
AI info = global::navigationHandler->joystickAxisMapping(axis);
|
||||
AI info = global::navigationHandler->joystickAxisMapping(joystickName, axis);
|
||||
|
||||
ghoul::lua::push(
|
||||
L,
|
||||
@@ -192,27 +194,32 @@ int joystickAxis(lua_State* L) {
|
||||
}
|
||||
|
||||
int setJoystickAxisDeadzone(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::setJoystickAxisDeadzone");
|
||||
auto [axis, deadzone] = ghoul::lua::values<int, float>(L);
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 3, "lua::setJoystickAxisDeadzone");
|
||||
auto [joystickName, axis, deadzone] = ghoul::lua::values<std::string, int, float>(L);
|
||||
|
||||
global::navigationHandler->setJoystickAxisDeadzone(axis, deadzone);
|
||||
global::navigationHandler->setJoystickAxisDeadzone(joystickName, axis, deadzone);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int joystickAxisDeadzone(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::joystickAxisDeadzone");
|
||||
const int axis = ghoul::lua::value<int>(L);
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::joystickAxisDeadzone");
|
||||
auto [joystickName, axis] = ghoul::lua::values<std::string, int>(L);
|
||||
|
||||
const float deadzone = global::navigationHandler->joystickAxisDeadzone(axis);
|
||||
const float deadzone = global::navigationHandler->joystickAxisDeadzone(joystickName, axis);
|
||||
ghoul::lua::push(L, deadzone);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bindJoystickButton(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, { 3, 5 }, "lua::bindJoystickButton");
|
||||
auto [button, command, documentation, actionStr, isRemote] =
|
||||
ghoul::lua::checkArgumentsAndThrow(L, { 4, 6 }, "lua::bindJoystickButton");
|
||||
auto [joystickName, button, command, documentation, actionStr, isRemote] =
|
||||
ghoul::lua::values<
|
||||
int, std::string, std::string, std::optional<std::string>, std::optional<bool>
|
||||
std::string,
|
||||
int,
|
||||
std::string,
|
||||
std::string,
|
||||
std::optional<std::string>,
|
||||
std::optional<bool>
|
||||
>(L);
|
||||
actionStr = actionStr.value_or("Press");
|
||||
isRemote = isRemote.value_or(true);
|
||||
@@ -221,6 +228,7 @@ int bindJoystickButton(lua_State* L) {
|
||||
ghoul::from_string<interaction::JoystickAction>(*actionStr);
|
||||
|
||||
global::navigationHandler->bindJoystickButtonCommand(
|
||||
joystickName,
|
||||
button,
|
||||
command,
|
||||
action,
|
||||
@@ -231,18 +239,19 @@ int bindJoystickButton(lua_State* L) {
|
||||
}
|
||||
|
||||
int clearJoystickButton(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::clearJoystickButton");
|
||||
const int button = ghoul::lua::value<int>(L);
|
||||
global::navigationHandler->clearJoystickButtonCommand(button);
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::clearJoystickButton");
|
||||
auto [joystickName, button] = ghoul::lua::values<std::string, int>(L);
|
||||
|
||||
global::navigationHandler->clearJoystickButtonCommand(joystickName, button);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int joystickButton(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::joystickButton");
|
||||
const int button = ghoul::lua::value<int>(L);
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::joystickButton");
|
||||
auto [joystickName, button] = ghoul::lua::values<std::string, int>(L);
|
||||
|
||||
const std::vector<std::string>& cmds =
|
||||
global::navigationHandler->joystickButtonCommand(button);
|
||||
global::navigationHandler->joystickButtonCommand(joystickName, button);
|
||||
|
||||
std::string cmd = std::accumulate(
|
||||
cmds.cbegin(),
|
||||
|
||||
Reference in New Issue
Block a user