Add initial support for SpaceMouse as joystick input

This commit is contained in:
Malin Ejdbo
2021-04-23 10:52:31 +02:00
parent 32b6a69900
commit 08118ba7bb
8 changed files with 184 additions and 80 deletions

View File

@@ -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)

View File

@@ -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<AxisInformation, JoystickInputState::MaxAxes> _axisMapping;
std::array<float, JoystickInputState::MaxAxes> _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; }

View File

@@ -71,6 +71,10 @@ 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. 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<JoystickInputState, MaxJoysticks>
*/
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

View File

@@ -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;

View File

@@ -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<float>(value * _sensitivity);
if (std::fabs(t.sensitivity) > std::numeric_limits<double>().epsilon()) {
value = static_cast<float>(value * t.sensitivity);
}
else {
value = static_cast<float>(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 {

View File

@@ -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");

View File

@@ -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
);
}

View File

@@ -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<bool>(L, 3) : false;
const bool shouldNormalize = n > 3 ? ghoul::lua::value<bool>(L, 4) : false;
const bool isStatic = n > 4 ? ghoul::lua::value<bool>(L, 5) : false;
const double sensitivity = n > 5 ? ghoul::lua::value<double>(L, 6) : 0.0;
global::navigationHandler->setJoystickAxisMapping(
axis,
ghoul::from_string<interaction::JoystickCameraStates::AxisType>(axisType),
interaction::JoystickCameraStates::AxisInvert(shouldInvert),
interaction::JoystickCameraStates::AxisNormalize(shouldNormalize)
interaction::JoystickCameraStates::AxisNormalize(shouldNormalize),
isStatic,
sensitivity
);
lua_settop(L, 0);