Merge pull request #1593 from OpenSpace/feature/space-mouse

Feature/space mouse
This commit is contained in:
Malin E
2021-05-24 14:01:05 +02:00
committed by GitHub
7 changed files with 198 additions and 92 deletions
+123 -69
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"
@@ -44,10 +45,10 @@ local PS4Controller = {
RightThumbStick = { 2, 5 },
LeftTrigger = 3,
RightTrigger = 4,
A = 3, -- Triangle
B = 0, -- Square
X = 2, -- Circle
Y = 1, -- Cross
A = 1, -- Cross
B = 2, -- Circle
X = 0, -- Square
Y = 3, -- Triangle
LB = 4,
RB = 5,
Select = 9, -- options
@@ -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 = {}
@@ -73,11 +82,11 @@ local bindLocalRoll = function(axis)
-- We only want to store the current state in the first mode that is enabled, otherwise we will overwrite the backup
if not Joystick.State.IsInRollMode then
-- Save current axis state
Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized = openspace.navigation.joystickAxis(]] .. axis .. [[)
Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity = openspace.navigation.joystickAxis(]] .. axis .. [[)
end
-- Set new axis state
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, "LocalRoll X", true);
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, "LocalRoll X", Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity);
Joystick.State.IsInRollMode = true
]]
end
@@ -87,88 +96,133 @@ local bindGlobalRoll = function(axis)
-- We only want to store the current state in the first mode that is enabled, otherwise we will overwrite the backup
if not Joystick.State.IsInRollMode then
-- Save current axis state
Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized = openspace.navigation.joystickAxis(]] .. axis .. [[)
Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity = openspace.navigation.joystickAxis(]] .. axis .. [[)
end
-- Set new axis state
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, "GlobalRoll X", true);
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, "GlobalRoll X", Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity);
Joystick.State.IsInRollMode = true
]]
end
local permaBindLocalRoll = function(axis)
return [[
-- Save current axis state
Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity = openspace.navigation.joystickAxis(]] .. axis .. [[)
-- Set new axis state
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, "LocalRoll X", Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity);
]]
end
local permaBindGlobalRoll = function(axis)
return [[
-- Save current axis state
Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity = openspace.navigation.joystickAxis(]] .. axis .. [[)
-- Set new axis state
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, "GlobalRoll X", Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity);
]]
end
local unbindRoll = function(axis)
return [[
-- Reset previous state
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized);
openspace.navigation.bindJoystickAxis(]] .. axis .. [[, Joystick.State.Axis.Type, Joystick.State.Axis.Inverted, Joystick.State.Axis.Normalized, Joystick.State.Axis.Sticky, Joystick.State.Axis.Sensitivity);
]]
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)
-- Case of XBoxController or PS4Controller
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.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.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Aim', '');" ..
"openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Anchor', 'Earth');" ..
"openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.RetargetAnchor', nil);",
"Switch target to Earth"
)
openspace.navigation.bindJoystickButton(
controller.Y,
"openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Aim', '');" ..
"openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.Anchor', 'Mars');" ..
"openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.RetargetAnchor', nil);",
"Switch target to Mars"
)
-- Case of SpaceMouse
elseif (controller.LeftButton ~= nil) then
openspace.navigation.bindJoystickAxis(controller.Push[1], "Orbit X", false, false, true, 40.0);
openspace.navigation.bindJoystickAxis(controller.Push[2], "Orbit Y", false, false, true, 40.0);
openspace.navigation.bindJoystickAxis(controller.Twist[1], "Pan X", true, false, true, 40.0);
openspace.navigation.bindJoystickAxis(controller.Tilt[2], "Pan Y", false, false, true, 35.0);
openspace.navigation.bindJoystickAxis(controller.Push[3], "Zoom", false, false, true, 40.0);
openspace.navigation.bindJoystickAxis(controller.Tilt[1], "LocalRoll X", false, false, true, 35.0);
openspace.navigation.bindJoystickButton(
controller.LeftButton,
permaBindLocalRoll(controller.Tilt[1]),
"Switch to local roll mode"
)
openspace.navigation.bindJoystickButton(
controller.RightButton,
permaBindGlobalRoll(controller.Tilt[1]),
"Switch to global roll mode"
)
end
end)
@@ -43,6 +43,7 @@ public:
OrbitY,
ZoomIn,
ZoomOut,
Zoom,
LocalRollX,
LocalRollY,
GlobalRollX,
@@ -60,7 +61,15 @@ 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 before the joystick was released.
// The latter is called a sticky axis, when the values don't go back to 0.
bool isSticky = false;
float deadzone = 0.f;
// Every axis can have their own sensitivity
double sensitivity = 0.0;
};
JoystickCameraStates(double sensitivity, double velocityScaleFactor);
@@ -69,7 +78,8 @@ public:
void setAxisMapping(int axis, AxisType mapping,
AxisInvert shouldInvert = AxisInvert::No,
AxisNormalize shouldNormalize = AxisNormalize::No
AxisNormalize shouldNormalize = AxisNormalize::No,
bool isSticky = false, double sensitivity = 0.0
);
AxisInformation axisMapping(int axis) const;
@@ -91,6 +101,10 @@ private:
std::array<AxisInformation, JoystickInputState::MaxAxes> _axisMapping;
// 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;
@@ -135,6 +149,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,11 @@ 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
@@ -115,7 +115,8 @@ public:
JoystickCameraStates::AxisInvert shouldInvert =
JoystickCameraStates::AxisInvert::No,
JoystickCameraStates::AxisNormalize shouldNormalize =
JoystickCameraStates::AxisNormalize::No
JoystickCameraStates::AxisNormalize::No,
bool isSticky = false, double sensitivity = 0.0
);
JoystickCameraStates::AxisInformation joystickAxisMapping(int axis) const;
+35 -14
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.isSticky) {
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::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 = 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 isSticky,
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].isSticky = isSticky;
_axisMapping[axis].sensitivity = sensitivity;
if (isSticky) {
global::joystickInputStates->at(axis).isSticky = true;
}
_prevAxisValues[axis] = global::joystickInputStates->axis(axis);
}
JoystickCameraStates::AxisInformation JoystickCameraStates::axisMapping(int axis) const {
+6 -2
View File
@@ -496,13 +496,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 isSticky,
double sensitivity)
{
_orbitalNavigator.joystickStates().setAxisMapping(
axis,
mapping,
shouldInvert,
shouldNormalize
shouldNormalize,
isSticky,
sensitivity
);
}
+11 -5
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 isSticky = 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),
isSticky,
sensitivity
);
lua_settop(L, 0);
@@ -223,10 +227,12 @@ int joystickAxis(lua_State* L) {
lua_settop(L, 0);
const bool invert = info.invert;
const bool normalize = info.normalize;
ghoul::lua::push(L, ghoul::to_string(info.type), invert, normalize);
const bool isSticky = info.isSticky;
const float sensitivity = info.sensitivity;
ghoul::lua::push(L, ghoul::to_string(info.type), invert, normalize, isSticky, sensitivity);
ghoul_assert(lua_gettop(L) == 3, "Incorrect number of items left on stack");
return 3;
ghoul_assert(lua_gettop(L) == 5, "Incorrect number of items left on stack");
return 5;
}
int setJoystickAxisDeadzone(lua_State* L) {