Add ability to bind a joystick axis to a property value

This commit is contained in:
Malin E
2021-11-11 09:27:54 +01:00
parent 2b4e1ae8fd
commit a515f85234
5 changed files with 257 additions and 119 deletions

View File

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

View File

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

View File

@@ -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<double>::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<std::string> JoystickCameraStates::buttonCommand(
std::vector<std::string> 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

View File

@@ -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",

View File

@@ -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<float>, std::optional<float>,
std::optional<bool>, std::optional<bool>, std::optional<double>,
std::optional<bool>
>(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<std::string, int>(L);
@@ -188,9 +218,13 @@ int joystickAxis(lua_State* L) {
static_cast<bool>(info.invert),
static_cast<bool>(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) {