From 08118ba7bb236fc8c0f35f5fa4158dc73775a19c Mon Sep 17 00:00:00 2001 From: Malin Ejdbo Date: Fri, 23 Apr 2021 10:52:31 +0200 Subject: [PATCH] Add initial support for SpaceMouse as joystick input --- data/assets/util/default_joystick.asset | 161 +++++++++++------- .../interaction/joystickcamerastates.h | 14 +- .../interaction/joystickinputstate.h | 10 ++ .../openspace/interaction/navigationhandler.h | 3 +- src/interaction/joystickcamerastates.cpp | 49 ++++-- src/interaction/joystickinputstate.cpp | 11 ++ src/interaction/navigationhandler.cpp | 8 +- src/interaction/navigationhandler_lua.inl | 8 +- 8 files changed, 184 insertions(+), 80 deletions(-) diff --git a/data/assets/util/default_joystick.asset b/data/assets/util/default_joystick.asset index 2c55c2dc42..de8c9be609 100644 --- a/data/assets/util/default_joystick.asset +++ b/data/assets/util/default_joystick.asset @@ -4,6 +4,7 @@ local propertyHelper = asset.require('./property_helper') -- "None" -- "Orbit X" -- "Orbit Y" +-- "Zoom" -- both in and out -- "Zoom In" -- "Zoom Out" -- "LocalRoll X" @@ -62,6 +63,14 @@ local PS4Controller = { } } +local SpaceMouse = { + Push = {0, 1, 2}, -- left/right, back/forth, up/down + Twist = {5}, -- left/right + Tilt = {4, 3}, -- left/right, back/forth + LeftButton = 0, + RightButton = 1 +} + -- Variables to store the state of the joystick between frames Joystick = {} Joystick.State = {} @@ -105,70 +114,102 @@ end asset.onInitialize(function() -- Set the controller to the connected controller - -- Currently: XBoxController or PS4Controller + -- Currently: XBoxController, PS4Controller or SpaceMouse local controller = XBoxController; - openspace.navigation.setAxisDeadZone(controller.LeftThumbStick[1], 0.15) - openspace.navigation.setAxisDeadZone(controller.LeftThumbStick[2], 0.15) - openspace.navigation.setAxisDeadZone(controller.RightThumbStick[1], 0.15) - openspace.navigation.setAxisDeadZone(controller.RightThumbStick[2], 0.15) - openspace.navigation.setAxisDeadZone(controller.LeftTrigger, 0.15) - openspace.navigation.setAxisDeadZone(controller.RightTrigger, 0.15) + if(controller.A ~= nil) then + openspace.navigation.setAxisDeadZone(controller.LeftThumbStick[1], 0.15) + openspace.navigation.setAxisDeadZone(controller.LeftThumbStick[2], 0.15) + openspace.navigation.setAxisDeadZone(controller.RightThumbStick[1], 0.15) + openspace.navigation.setAxisDeadZone(controller.RightThumbStick[2], 0.15) + openspace.navigation.setAxisDeadZone(controller.LeftTrigger, 0.15) + openspace.navigation.setAxisDeadZone(controller.RightTrigger, 0.15) - openspace.navigation.bindJoystickAxis(controller.LeftThumbStick[1], "Orbit X"); - openspace.navigation.bindJoystickAxis(controller.LeftThumbStick[2], "Orbit Y", true); - openspace.navigation.bindJoystickAxis(controller.RightThumbStick[1], "Pan X", true); - openspace.navigation.bindJoystickAxis(controller.RightThumbStick[2], "Pan Y", true); - openspace.navigation.bindJoystickAxis(controller.LeftTrigger, "Zoom Out", false, true); - openspace.navigation.bindJoystickAxis(controller.RightTrigger, "Zoom In", false, true); + openspace.navigation.bindJoystickAxis(controller.LeftThumbStick[1], "Orbit X"); + openspace.navigation.bindJoystickAxis(controller.LeftThumbStick[2], "Orbit Y", true); + openspace.navigation.bindJoystickAxis(controller.RightThumbStick[1], "Pan X", true); + openspace.navigation.bindJoystickAxis(controller.RightThumbStick[2], "Pan Y", true); + openspace.navigation.bindJoystickAxis(controller.LeftTrigger, "Zoom Out", false, true); + openspace.navigation.bindJoystickAxis(controller.RightTrigger, "Zoom In", false, true); - openspace.navigation.bindJoystickButton( - controller.LB, - bindLocalRoll(controller.RightThumbStick[1]), - "Switch to local roll mode" - ) - openspace.navigation.bindJoystickButton( - controller.LB, - unbindRoll(controller.RightThumbStick[1]), - "Switch back to normal mode", - "Release" - ) - openspace.navigation.bindJoystickButton( - controller.RB, - bindGlobalRoll(controller.RightThumbStick[1]), - "Switch to global roll mode" - ) - openspace.navigation.bindJoystickButton( - controller.RB, - unbindRoll(controller.RightThumbStick[1]), - "Switch back to normal mode", - "Release" - ) + openspace.navigation.bindJoystickButton( + controller.LB, + bindLocalRoll(controller.RightThumbStick[1]), + "Switch to local roll mode" + ) + openspace.navigation.bindJoystickButton( + controller.LB, + unbindRoll(controller.RightThumbStick[1]), + "Switch back to normal mode", + "Release" + ) + openspace.navigation.bindJoystickButton( + controller.RB, + bindGlobalRoll(controller.RightThumbStick[1]), + "Switch to global roll mode" + ) + openspace.navigation.bindJoystickButton( + controller.RB, + unbindRoll(controller.RightThumbStick[1]), + "Switch back to normal mode", + "Release" + ) - openspace.navigation.bindJoystickButton( - controller.A, - propertyHelper.invert('NavigationHandler.OrbitalNavigator.Friction.ZoomFriction'), - "Toggle zoom friction" - ) - openspace.navigation.bindJoystickButton( - controller.B, - propertyHelper.invert('NavigationHandler.OrbitalNavigator.Friction.RotationalFriction'), - "Toggle rotational friction" - ) - openspace.navigation.bindJoystickButton( - controller.DPad.Left, - propertyHelper.invert('NavigationHandler.OrbitalNavigator.Friction.RollFriction'), - "Toggle roll friction" - ) + openspace.navigation.bindJoystickButton( + controller.A, + propertyHelper.invert('NavigationHandler.OrbitalNavigator.Friction.ZoomFriction'), + "Toggle zoom friction" + ) + openspace.navigation.bindJoystickButton( + controller.B, + propertyHelper.invert('NavigationHandler.OrbitalNavigator.Friction.RotationalFriction'), + "Toggle rotational friction" + ) + openspace.navigation.bindJoystickButton( + controller.DPad.Left, + propertyHelper.invert('NavigationHandler.OrbitalNavigator.Friction.RollFriction'), + "Toggle roll friction" + ) - openspace.navigation.bindJoystickButton( - controller.X, - "openspace.setPropertyValue('NavigationHandler.Origin', 'Earth')", - "Switch target to Earth" - ) - openspace.navigation.bindJoystickButton( - controller.Y, - "openspace.setPropertyValue('NavigationHandler.Origin', 'Mars')", - "Switch target to Mars" - ) + openspace.navigation.bindJoystickButton( + controller.X, + "openspace.setPropertyValue('NavigationHandler.Origin', 'Earth')", + "Switch target to Earth" + ) + openspace.navigation.bindJoystickButton( + controller.Y, + "openspace.setPropertyValue('NavigationHandler.Origin', 'Mars')", + "Switch target to Mars" + ) + elseif (controller.LeftButton ~= nil) then + openspace.navigation.bindJoystickAxis(controller.Push[1], "Orbit X", false, false, true, 25.0); + openspace.navigation.bindJoystickAxis(controller.Push[2], "Orbit Y", false, false, true, 25.0); + openspace.navigation.bindJoystickAxis(controller.Twist[1], "Pan X", true, false, true, 25.0); + openspace.navigation.bindJoystickAxis(controller.Tilt[2], "Pan Y", false, false, true, 25.0); + openspace.navigation.bindJoystickAxis(controller.Push[3], "Zoom", false, false, true, 25.0); + + openspace.navigation.bindJoystickButton( + controller.LeftButton, + bindLocalRoll(controller.Twist[1]), + "Switch to local roll mode" + ) + openspace.navigation.bindJoystickButton( + controller.LeftButton, + unbindRoll(controller.Twist[1]), + "Switch back to normal mode", + "Release" + ) + + openspace.navigation.bindJoystickButton( + controller.RightButton, + bindGlobalRoll(controller.Twist[1]), + "Switch to global roll mode" + ) + openspace.navigation.bindJoystickButton( + controller.RightButton, + unbindRoll(controller.Twist[1]), + "Switch back to normal mode", + "Release" + ) + end end) diff --git a/include/openspace/interaction/joystickcamerastates.h b/include/openspace/interaction/joystickcamerastates.h index fd0098e933..da12dc7081 100644 --- a/include/openspace/interaction/joystickcamerastates.h +++ b/include/openspace/interaction/joystickcamerastates.h @@ -43,6 +43,7 @@ public: OrbitY, ZoomIn, ZoomOut, + Zoom, LocalRollX, LocalRollY, GlobalRollX, @@ -60,7 +61,14 @@ public: AxisInvert invert = AxisInvert::No; AxisNormalize normalize = AxisNormalize::No; + // The axis values can either go back to 0 when the joystick is released or it can + // stay at the value it was. The latter is static + bool isStatic = false; + float deadzone = 0.f; + + // Every axis can have their own sensitivity + double sensitivity = 0.0; }; JoystickCameraStates(double sensitivity, double velocityScaleFactor); @@ -69,7 +77,8 @@ public: void setAxisMapping(int axis, AxisType mapping, AxisInvert shouldInvert = AxisInvert::No, - AxisNormalize shouldNormalize = AxisNormalize::No + AxisNormalize shouldNormalize = AxisNormalize::No, + bool isStatic = false, double sensitivity = 0.0 ); AxisInformation axisMapping(int axis) const; @@ -91,6 +100,8 @@ private: std::array _axisMapping; + std::array _prevAxisValues; + struct ButtonInformation { std::string command; JoystickAction action; @@ -135,6 +146,7 @@ from_string(std::string_view string) if (string == "None") { return T::None; } if (string == "Orbit X") { return T::OrbitX; } if (string == "Orbit Y") { return T::OrbitY; } + if (string == "Zoom") { return T::Zoom; } if (string == "Zoom In") { return T::ZoomIn; } if (string == "Zoom Out") { return T::ZoomOut; } if (string == "LocalRoll X") { return T::LocalRollX; } diff --git a/include/openspace/interaction/joystickinputstate.h b/include/openspace/interaction/joystickinputstate.h index ced81f4013..48586c84ff 100644 --- a/include/openspace/interaction/joystickinputstate.h +++ b/include/openspace/interaction/joystickinputstate.h @@ -71,6 +71,10 @@ struct JoystickInputState { /// \c nAxes values are defined values, the rest are undefined std::array axes; + /// The axis values can either go back to 0 when the joystick is released or it can + /// stay at the value it was. The latter is static + bool isStatic = 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 @@ -95,6 +99,12 @@ struct JoystickInputStates : public std::array */ float axis(int axis) const; + /** + * This function checks whether this joystick input has static axis or not + * \return If this joystick input has static axis or not + */ + bool isStatic() const; + /** * This functions checks whether any connected joystick has its \p button in the * passed \p action. Any joystick that does not posses the \p button, it will be diff --git a/include/openspace/interaction/navigationhandler.h b/include/openspace/interaction/navigationhandler.h index 68d34498b2..a6b06b2633 100644 --- a/include/openspace/interaction/navigationhandler.h +++ b/include/openspace/interaction/navigationhandler.h @@ -115,7 +115,8 @@ public: JoystickCameraStates::AxisInvert shouldInvert = JoystickCameraStates::AxisInvert::No, JoystickCameraStates::AxisNormalize shouldNormalize = - JoystickCameraStates::AxisNormalize::No + JoystickCameraStates::AxisNormalize::No, + bool isStatic = false, double sensitivity = 0.0 ); JoystickCameraStates::AxisInformation joystickAxisMapping(int axis) const; diff --git a/src/interaction/joystickcamerastates.cpp b/src/interaction/joystickcamerastates.cpp index 78a9f97506..fe09ecc85c 100644 --- a/src/interaction/joystickcamerastates.cpp +++ b/src/interaction/joystickcamerastates.cpp @@ -52,8 +52,13 @@ void JoystickCameraStates::updateStateFromInput(const InputState& inputState, continue; } - bool hasValue = true; - float value = inputState.joystickAxis(i); + float rawValue = inputState.joystickAxis(i); + float value = rawValue; + + if (t.isStatic) { + value = rawValue - _prevAxisValues[i]; + _prevAxisValues[i] = rawValue; + } if (std::fabs(value) <= t.deadzone) { continue; @@ -67,49 +72,55 @@ void JoystickCameraStates::updateStateFromInput(const InputState& inputState, value *= -1.f; } - value = static_cast(value * _sensitivity); + if (std::fabs(t.sensitivity) > std::numeric_limits().epsilon()) { + value = static_cast(value * t.sensitivity); + } + else { + value = static_cast(value * _sensitivity); + } switch (t.type) { case AxisType::None: break; case AxisType::OrbitX: - globalRotation.first = hasValue; + globalRotation.first = true; globalRotation.second.x = value; break; case AxisType::OrbitY: - globalRotation.first = hasValue; + globalRotation.first = true; globalRotation.second.y = value; break; + case AxisType::Zoom: case AxisType::ZoomIn: - zoom.first = hasValue; + zoom.first = true; zoom.second += value; break; case AxisType::ZoomOut: - zoom.first = hasValue; + zoom.first = true; zoom.second -= value; break; case AxisType::LocalRollX: - localRoll.first = hasValue; + localRoll.first = true; localRoll.second.x = value; break; case AxisType::LocalRollY: - localRoll.first = hasValue; + localRoll.first = true; localRoll.second.y = value; break; case AxisType::GlobalRollX: - globalRoll.first = hasValue; + globalRoll.first = true; globalRoll.second.x = value; break; case AxisType::GlobalRollY: - globalRoll.first = hasValue; + globalRoll.first = true; globalRoll.second.y = value; break; case AxisType::PanX: - localRotation.first = hasValue; + localRotation.first = true; localRotation.second.x = value; break; case AxisType::PanY: - localRotation.first = hasValue; + localRotation.first = true; localRotation.second.y = value; break; } @@ -167,13 +178,23 @@ void JoystickCameraStates::updateStateFromInput(const InputState& inputState, void JoystickCameraStates::setAxisMapping(int axis, AxisType mapping, AxisInvert shouldInvert, - AxisNormalize shouldNormalize) + AxisNormalize shouldNormalize, + bool isStatic, + double sensitivity) { ghoul_assert(axis < JoystickInputState::MaxAxes, "axis must be < MaxAxes"); _axisMapping[axis].type = mapping; _axisMapping[axis].invert = shouldInvert; _axisMapping[axis].normalize = shouldNormalize; + _axisMapping[axis].isStatic = isStatic; + _axisMapping[axis].sensitivity = sensitivity; + + if (isStatic) { + global::joystickInputStates->at(axis).isStatic = true; + } + + _prevAxisValues[axis] = 0.f; } JoystickCameraStates::AxisInformation JoystickCameraStates::axisMapping(int axis) const { diff --git a/src/interaction/joystickinputstate.cpp b/src/interaction/joystickinputstate.cpp index 4a5537a353..c052806523 100644 --- a/src/interaction/joystickinputstate.cpp +++ b/src/interaction/joystickinputstate.cpp @@ -54,6 +54,17 @@ float JoystickInputStates::axis(int axis) const { return res; } +bool JoystickInputStates::isStatic() const { + bool res = std::any_of( + begin(), + end(), + [](const JoystickInputState& state) { + return state.isStatic; + } + ); + return res; +} + bool JoystickInputStates::button(int button, JoystickAction action) const { ghoul_precondition(button >= 0, "button must be 0 or positive"); diff --git a/src/interaction/navigationhandler.cpp b/src/interaction/navigationhandler.cpp index 203a674f1e..b22b321940 100644 --- a/src/interaction/navigationhandler.cpp +++ b/src/interaction/navigationhandler.cpp @@ -492,13 +492,17 @@ void NavigationHandler::loadNavigationState(const std::string& filepath) { void NavigationHandler::setJoystickAxisMapping(int axis, JoystickCameraStates::AxisType mapping, JoystickCameraStates::AxisInvert shouldInvert, - JoystickCameraStates::AxisNormalize shouldNormalize) + JoystickCameraStates::AxisNormalize shouldNormalize, + bool isStatic, + double sensitivity) { _orbitalNavigator.joystickStates().setAxisMapping( axis, mapping, shouldInvert, - shouldNormalize + shouldNormalize, + isStatic, + sensitivity ); } diff --git a/src/interaction/navigationhandler_lua.inl b/src/interaction/navigationhandler_lua.inl index 34f6531f51..504526ab2c 100644 --- a/src/interaction/navigationhandler_lua.inl +++ b/src/interaction/navigationhandler_lua.inl @@ -190,7 +190,7 @@ int retargetAim(lua_State* L) { int bindJoystickAxis(lua_State* L) { const int n = ghoul::lua::checkArgumentsAndThrow( L, - { 2, 4 }, + { 2, 6 }, "lua::bindJoystickAxis" ); @@ -199,12 +199,16 @@ int bindJoystickAxis(lua_State* L) { const bool shouldInvert = n > 2 ? ghoul::lua::value(L, 3) : false; const bool shouldNormalize = n > 3 ? ghoul::lua::value(L, 4) : false; + const bool isStatic = n > 4 ? ghoul::lua::value(L, 5) : false; + const double sensitivity = n > 5 ? ghoul::lua::value(L, 6) : 0.0; global::navigationHandler->setJoystickAxisMapping( axis, ghoul::from_string(axisType), interaction::JoystickCameraStates::AxisInvert(shouldInvert), - interaction::JoystickCameraStates::AxisNormalize(shouldNormalize) + interaction::JoystickCameraStates::AxisNormalize(shouldNormalize), + isStatic, + sensitivity ); lua_settop(L, 0);