mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-05 11:09:37 -06:00
Add initial support for SpaceMouse as joystick input
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user