From a515f852345b8e9cd281aff140ecebe1bc996ee8 Mon Sep 17 00:00:00 2001 From: Malin E Date: Thu, 11 Nov 2021 09:27:54 +0100 Subject: [PATCH] Add ability to bind a joystick axis to a property value --- .../interaction/joystickcamerastates.h | 20 +- .../openspace/navigation/navigationhandler.h | 8 + src/interaction/joystickcamerastates.cpp | 206 ++++++++++-------- src/navigation/navigationhandler.cpp | 104 ++++++--- src/navigation/navigationhandler_lua.inl | 38 +++- 5 files changed, 257 insertions(+), 119 deletions(-) diff --git a/include/openspace/interaction/joystickcamerastates.h b/include/openspace/interaction/joystickcamerastates.h index 2ac5aa2f71..5c7d5a3171 100644 --- a/include/openspace/interaction/joystickcamerastates.h +++ b/include/openspace/interaction/joystickcamerastates.h @@ -49,7 +49,8 @@ public: GlobalRollX, GlobalRollY, PanX, - PanY + PanY, + Property }; BooleanType(AxisInvert); @@ -70,6 +71,12 @@ public: // Every axis can have their own sensitivity double sensitivity = 0.0; + + // The property info if the type is Property + std::string propertyUri; + float minValue = 0.f; + float maxValue = 1.f; + bool isRemote = true; }; JoystickCameraStates(double sensitivity, double velocityScaleFactor); @@ -83,6 +90,12 @@ public: bool isSticky = false, double sensitivity = 0.0 ); + void setAxisMappingProperty(const std::string& joystickName, int axis, + const std::string& propertyUri, float min = 0.f, float max = 1.f, + AxisInvert shouldInvert = AxisInvert::No, + bool isSticky = false, double sensitivity = 0.0, bool isRemote = true + ); + AxisInformation axisMapping(const std::string& joystickName, int axis) const; void setDeadzone(const std::string& joystickName, int axis, float deadzone); @@ -126,6 +139,10 @@ private: // 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; + + // Ues getJoystickCameraState(name) to find the joystickCameraState that + // corresponds to the given joystickName. If not found then add a new item if possible + JoystickCameraState* findOrAddJoystickCameraState(const std::string& joystickName); }; } // namespace openspace::interaction @@ -172,6 +189,7 @@ from_string(std::string_view string) if (string == "GlobalRoll Y") { return T::GlobalRollY; } if (string == "Pan X") { return T::PanX; } if (string == "Pan Y") { return T::PanY; } + if (string == "Property") { return T::Property; } throw RuntimeError("Unkonwn axis type '" + std::string(string) + "'"); } diff --git a/include/openspace/navigation/navigationhandler.h b/include/openspace/navigation/navigationhandler.h index 5fa4c105b4..3b2566b704 100644 --- a/include/openspace/navigation/navigationhandler.h +++ b/include/openspace/navigation/navigationhandler.h @@ -107,6 +107,14 @@ public: bool isSticky = false, double sensitivity = 0.0 ); + void setJoystickAxisMappingProperty(const std::string& joystickName, + int axis, const std::string& propertyUri, + float min = 0.f, float max = 1.f, + JoystickCameraStates::AxisInvert shouldInvert = + JoystickCameraStates::AxisInvert::No, + bool isSticky = false, double sensitivity = 0.0, bool isRemote = true + ); + JoystickCameraStates::AxisInformation joystickAxisMapping( const std::string& joystickName, int axis) const; diff --git a/src/interaction/joystickcamerastates.cpp b/src/interaction/joystickcamerastates.cpp index 2eadf18a61..74ad9adaab 100644 --- a/src/interaction/joystickcamerastates.cpp +++ b/src/interaction/joystickcamerastates.cpp @@ -81,12 +81,16 @@ void JoystickCameraStates::updateStateFromInput( continue; } - if (t.normalize) { + if (t.invert) { + value *= -1.f; + } + + if (t.normalize || t.type == AxisType::Property) { value = (value + 1.f) / 2.f; } - if (t.invert) { - value *= -1.f; + if (t.type == AxisType::Property) { + value = value * (t.maxValue - t.minValue) + t.minValue; } if (std::abs(t.sensitivity) > std::numeric_limits::epsilon()) { @@ -97,56 +101,65 @@ void JoystickCameraStates::updateStateFromInput( } switch (t.type) { - case AxisType::None: - break; - case AxisType::OrbitX: - globalRotation.first = true; - globalRotation.second.x += value; - break; - case AxisType::OrbitY: - globalRotation.first = true; - globalRotation.second.y += value; - break; - case AxisType::Zoom: - case AxisType::ZoomIn: - zoom.first = true; - zoom.second += value; - break; - case AxisType::ZoomOut: - zoom.first = true; - zoom.second -= value; - break; - case AxisType::LocalRollX: - localRoll.first = true; - localRoll.second.x += value; - break; - case AxisType::LocalRollY: - localRoll.first = true; - localRoll.second.y += value; - break; - case AxisType::GlobalRollX: - globalRoll.first = true; - globalRoll.second.x += value; - break; - case AxisType::GlobalRollY: - globalRoll.first = true; - globalRoll.second.y += value; - break; - case AxisType::PanX: - localRotation.first = true; - localRotation.second.x += value; - break; - case AxisType::PanY: - localRotation.first = true; - localRotation.second.y += value; - break; + case AxisType::None: + break; + case AxisType::OrbitX: + globalRotation.first = true; + globalRotation.second.x += value; + break; + case AxisType::OrbitY: + globalRotation.first = true; + globalRotation.second.y += value; + break; + case AxisType::Zoom: + case AxisType::ZoomIn: + zoom.first = true; + zoom.second += value; + break; + case AxisType::ZoomOut: + zoom.first = true; + zoom.second -= value; + break; + case AxisType::LocalRollX: + localRoll.first = true; + localRoll.second.x += value; + break; + case AxisType::LocalRollY: + localRoll.first = true; + localRoll.second.y += value; + break; + case AxisType::GlobalRollX: + globalRoll.first = true; + globalRoll.second.x += value; + break; + case AxisType::GlobalRollY: + globalRoll.first = true; + globalRoll.second.y += value; + break; + case AxisType::PanX: + localRotation.first = true; + localRotation.second.x += value; + break; + case AxisType::PanY: + localRotation.first = true; + localRotation.second.y += value; + break; + case AxisType::Property: + std::string script = "openspace.setPropertyValue(\"" + + t.propertyUri + "\", " + std::to_string(value) + ");"; + + global::scriptEngine->queueScript( + script, + scripting::ScriptEngine::RemoteScripting(t.isRemote) + ); + 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( + bool active = global::joystickInputStates->button( joystickInputState.name, i, it->second.action @@ -207,18 +220,9 @@ void JoystickCameraStates::setAxisMapping(const std::string& joystickName, { ghoul_assert(axis < JoystickInputState::MaxAxes, "axis must be < MaxAxes"); - JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName); + JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(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; - } + return; } joystickCameraState->axisMapping[axis].type = mapping; @@ -231,14 +235,40 @@ void JoystickCameraStates::setAxisMapping(const std::string& joystickName, global::joystickInputStates->axis(joystickName, axis); } +void JoystickCameraStates::setAxisMappingProperty(const std::string& joystickName, + int axis, + const std::string& propertyUri, + float min, float max, + AxisInvert shouldInvert, + bool isSticky, double sensitivity, + bool isRemote) +{ + ghoul_assert(axis < JoystickInputState::MaxAxes, "axis must be < MaxAxes"); + + JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(joystickName); + if (!joystickCameraState) { + return; + } + + joystickCameraState->axisMapping[axis].type = AxisType::Property; + joystickCameraState->axisMapping[axis].invert = shouldInvert; + joystickCameraState->axisMapping[axis].isSticky = isSticky; + joystickCameraState->axisMapping[axis].sensitivity = sensitivity; + joystickCameraState->axisMapping[axis].propertyUri = propertyUri; + joystickCameraState->axisMapping[axis].minValue = min; + joystickCameraState->axisMapping[axis].maxValue = max; + joystickCameraState->axisMapping[axis].isRemote = isRemote; + + joystickCameraState->prevAxisValues[axis] = + global::joystickInputStates->axis(joystickName, 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; } @@ -249,18 +279,9 @@ JoystickCameraStates::AxisInformation JoystickCameraStates::axisMapping( void JoystickCameraStates::setDeadzone(const std::string& joystickName, int axis, float deadzone) { - JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName); + JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(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; - } + return; } joystickCameraState->axisMapping[axis].deadzone = deadzone; @@ -269,8 +290,6 @@ void JoystickCameraStates::setDeadzone(const std::string& joystickName, int axis 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; } @@ -283,18 +302,9 @@ void JoystickCameraStates::bindButtonCommand(const std::string& joystickName, ButtonCommandRemote remote, std::string documentation) { - JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName); + JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(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; - } + return; } joystickCameraState->buttonMapping.insert({ @@ -308,8 +318,6 @@ void JoystickCameraStates::clearButtonCommand(const std::string& joystickName, { JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName); if (!joystickCameraState) { - LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}' " - "(clearButtonCommand)", joystickName)); return; } @@ -334,8 +342,6 @@ std::vector JoystickCameraStates::buttonCommand( std::vector result; const JoystickCameraState* joystickCameraState = getJoystickCameraState(joystickName); if (!joystickCameraState) { - LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}' " - "(buttonCommand)", joystickName)); return result; } @@ -358,8 +364,8 @@ JoystickCameraStates::JoystickCameraState* JoystickCameraStates::getJoystickCame return nullptr; } -const JoystickCameraStates::JoystickCameraState* JoystickCameraStates::getJoystickCameraState( - const std::string& joystickName) const +const JoystickCameraStates::JoystickCameraState* + JoystickCameraStates::getJoystickCameraState(const std::string& joystickName) const { for (const JoystickCameraState& joystickCameraState : _joystickCameraStates) { if (joystickCameraState.joystickName == joystickName) { @@ -367,8 +373,28 @@ const JoystickCameraStates::JoystickCameraState* JoystickCameraStates::getJoysti } } + LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}'", joystickName)); return nullptr; } +JoystickCameraStates::JoystickCameraState* + JoystickCameraStates::findOrAddJoystickCameraState(const std::string& joystickName) +{ + 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 nullptr; + } + } + return joystickCameraState; +} + } // namespace openspace::interaction diff --git a/src/navigation/navigationhandler.cpp b/src/navigation/navigationhandler.cpp index 8a2d67ae9f..4d840c07f4 100644 --- a/src/navigation/navigationhandler.cpp +++ b/src/navigation/navigationhandler.cpp @@ -516,6 +516,27 @@ void NavigationHandler::setJoystickAxisMapping(const std::string& joystickName, ); } +void NavigationHandler::setJoystickAxisMappingProperty(const std::string& joystickName, + int axis, + const std::string& propertyUri, + float min, float max, + JoystickCameraStates::AxisInvert shouldInvert, + bool isSticky, double sensitivity, + bool isRemote) +{ + _orbitalNavigator.joystickStates().setAxisMappingProperty( + joystickName, + axis, + propertyUri, + min, + max, + shouldInvert, + isSticky, + sensitivity, + isRemote + ); +} + void NavigationHandler::setWebsocketAxisMapping(int axis, WebsocketCameraStates::AxisType mapping, WebsocketCameraStates::AxisInvert shouldInvert, @@ -638,57 +659,88 @@ scripting::LuaLibrary NavigationHandler::luaLibrary() { "bindJoystickAxis", &luascriptfunctions::bindJoystickAxis, {}, - "int, axisType [, isInverted, isNormalized]", - "Binds the axis identified by the first argument to be used as the type " - "identified by the second argument. If 'isInverted' is 'true', the axis " - "value is inverted, if 'isNormalized' is true the axis value is " - "normalized from [-1, 1] to [0,1]." + "name, axis, axisType [, isInverted, isNormalized, isSticky, sensitivity]", + "Finds the input joystick with the given 'name' and binds the axis " + "identified by the second argument to be used as the type identified by " + "the third argument. If 'isInverted' is 'true', the axis value is " + "inverted, if 'isNormalized' is true the axis value is normalized from " + "[-1, 1] to [0,1]. If 'isSticky' is 'true', the value is calculated " + "relative to the previous value. If 'sensitivity' is given then that " + "value will affect the sensitivity of the axis together with " + "the global sensitivity." + }, + { + "bindJoystickAxisProperty", + &luascriptfunctions::bindJoystickAxisProperty, + {}, + "name, axis, propertyUri [, min, max, isInverted, isSticky, sensitivity, " + "isRemote]", + "Finds the input joystick with the given 'name' and binds the axis " + "identified by the second argument to be bound to the property " + "identified by the third argument. 'min' and 'max' is the minimum and " + "the maximum allowed value for the given property and the axis value is " + "rescaled from [-1, 1] to [min, max], default is [0, 1]. If 'isInverted' " + "is 'true', the axis value is inverted. If 'isSticky' is 'true', the " + "value is calculated relative to the previous value. If 'sensitivity' is " + "given then that value will affect the sensitivity of the axis together " + "with the global sensitivity. The last argument determines whether the " + "property change is going to be executed locally or remotely, where the " + "latter is the default." }, { "joystickAxis", &luascriptfunctions::joystickAxis, {}, - "int", - "Returns the joystick axis information for the passed axis. The " - "information that is returned is the current axis binding as a string, " - "whether the values are inverted as bool, and whether the value are " - "normalized as a bool" + "name, axis", + "Finds the input joystick with the given 'name' and returns the joystick " + "axis information for the passed axis. The information that is returned " + "is the current axis binding as a string, whether the values are " + "inverted as bool, whether the value are normalized as a bool, whether " + "the axis is sticky as bool, the sensitivity as number, the property uri " + "bound to the axis as string (empty is type is not Property), the min " + "and max values for the property as numbers and whether the property " + "change will be executed remotly as bool." }, { "setAxisDeadZone", &luascriptfunctions::setJoystickAxisDeadzone, {}, - "int, float", - "Sets the deadzone for a particular joystick axis which means that any " - "input less than this value is completely ignored." + "name, axis, float", + "Finds the input joystick with the given 'name' and sets the deadzone " + "for a particular joystick axis, which means that any input less than " + "this value is completely ignored." }, { "bindJoystickButton", &luascriptfunctions::bindJoystickButton, {}, - "int, string [, string, bool]", - "Binds a Lua script to be executed when the joystick button identified " - "by the first argument is triggered. The third argument determines when " - "the script should be executed, this defaults to 'pressed', which means " - "that the script is run when the user presses the button. The last " - "argument determines whether the command is going to be executable " - "locally or remotely. The latter being the default." + "name, button, string [, string, string, bool]", + "Finds the input joystick with the given 'name' and binds a Lua script " + "given by the third argument to be executed when the joystick button " + "identified by the second argument is triggered. The fifth argument " + "determines when the script should be executed, this defaults to " + "'Press', which means that the script is run when the user presses the " + "button. The fourth arguemnt is the documentation of the script in the " + "third argument. The sixth argument determines whether the command is " + "going to be executable locally or remotely, where the latter is the " + "default." }, { "clearJoystickButton", &luascriptfunctions::clearJoystickButton, {}, - "int", - "Removes all commands that are currently bound to the button identified " - "by the first argument" + "name, button", + "Finds the input joystick with the given 'name' and removes all commands " + "that are currently bound to the button identified by the second argument." }, { "joystickButton", &luascriptfunctions::joystickButton, {}, - "int", - "Returns the script that is currently bound to be executed when the " - "provided button is pressed" + "name, button", + "Finds the input joystick with the given 'name' and returns the script " + "that is currently bound to be executed when the provided button is " + "pressed." }, { "addGlobalRotation", diff --git a/src/navigation/navigationhandler_lua.inl b/src/navigation/navigationhandler_lua.inl index 59795a2bf9..d47b0a8318 100644 --- a/src/navigation/navigationhandler_lua.inl +++ b/src/navigation/navigationhandler_lua.inl @@ -175,6 +175,36 @@ int bindJoystickAxis(lua_State* L) { return 0; } +int bindJoystickAxisProperty(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, { 3, 9 }, "lua::bindJoystickAxisProperty"); + auto [joystickName, axis, propertyUri, min, max, shouldInvert, isSticky, sensitivity, + isRemote] = + ghoul::lua::values< + std::string, int, std::string, std::optional, std::optional, + std::optional, std::optional, std::optional, + std::optional + >(L); + min = min.value_or(0.f); + max = max.value_or(1.f); + shouldInvert = shouldInvert.value_or(false); + isSticky = isSticky.value_or(false); + sensitivity = sensitivity.value_or(0.0); + isRemote = isRemote.value_or(true); + + global::navigationHandler->setJoystickAxisMappingProperty( + joystickName, + axis, + propertyUri, + *min, + *max, + interaction::JoystickCameraStates::AxisInvert(*shouldInvert), + *isSticky, + *sensitivity, + *isRemote + ); + return 0; +} + int joystickAxis(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::joystickAxis"); auto [joystickName, axis] = ghoul::lua::values(L); @@ -188,9 +218,13 @@ int joystickAxis(lua_State* L) { static_cast(info.invert), static_cast(info.normalize), info.isSticky, - info.sensitivity + info.sensitivity, + info.propertyUri, + info.minValue, + info.maxValue, + info.isRemote ); - return 5; + return 9; } int setJoystickAxisDeadzone(lua_State* L) {