Files
OpenSpace/src/interaction/joystickcamerastates.cpp
T
2022-04-27 10:57:09 +02:00

435 lines
16 KiB
C++

/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <openspace/interaction/joystickcamerastates.h>
#include <openspace/engine/globals.h>
#include <openspace/scripting/scriptengine.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/exception.h>
#include <cmath>
#include <utility>
namespace {
constexpr const char* _loggerCat = "JoystickCameraStates";
} // namespace
namespace openspace::interaction {
JoystickCameraStates::JoystickCameraStates(double sensitivity, double velocityScaleFactor)
: CameraInteractionStates(sensitivity, velocityScaleFactor)
{}
void JoystickCameraStates::updateStateFromInput(
const JoystickInputStates& joystickInputStates,
double deltaTime)
{
std::pair<bool, glm::dvec2> globalRotation = { false, glm::dvec2(0.0) };
std::pair<bool, double> zoom = { false, 0.0 };
std::pair<bool, glm::dvec2> localRoll = { false, glm::dvec2(0.0) };
std::pair<bool, glm::dvec2> globalRoll = { false, glm::dvec2(0.0) };
std::pair<bool, glm::dvec2> localRotation = { false, glm::dvec2(0.0) };
for (const JoystickInputState& joystickInputState : joystickInputStates) {
if (joystickInputState.name.empty()) {
continue;
}
JoystickCameraState* joystick = joystickCameraState(joystickInputState.name);
if (!joystick) {
continue;
}
int nAxes = joystickInputStates.numAxes(joystickInputState.name);
for (int i = 0; i < nAxes; ++i) {
AxisInformation t = joystick->axisMapping[i];
if (t.type == AxisType::None) {
continue;
}
float rawValue = joystickInputStates.axis(joystickInputState.name, i);
float value = rawValue;
if (t.isSticky) {
value = rawValue - joystick->prevAxisValues[i];
joystick->prevAxisValues[i] = rawValue;
}
if ((t.joystickType == JoystickType::JoystickLike &&
std::abs(value) <= t.deadzone) ||
(
t.joystickType == JoystickType::TriggerLike &&
value <= -1.f + t.deadzone
))
{
continue;
}
if (t.invert) {
value *= -1.f;
}
if (t.joystickType == JoystickType::TriggerLike ||
t.type == AxisType::Property)
{
value = (value + 1.f) / 2.f;
}
if (t.type == AxisType::Property) {
value = value * (t.maxValue - t.minValue) + t.minValue;
}
else {
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 = 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 = fmt::format(
"openspace.setPropertyValue('{}', {});",
t.propertyUri, value
);
global::scriptEngine->queueScript(
script,
scripting::ScriptEngine::RemoteScripting(t.isRemote)
);
break;
}
}
int nButtons = joystickInputStates.numButtons(joystickInputState.name);
for (int i = 0; i < nButtons; ++i) {
auto itRange = joystick->buttonMapping.equal_range(i);
for (auto it = itRange.first; it != itRange.second; ++it) {
bool active = global::joystickInputStates->button(
joystickInputState.name,
i,
it->second.action
);
if (active) {
global::scriptEngine->queueScript(
it->second.command,
scripting::ScriptEngine::RemoteScripting(
it->second.synchronization
)
);
}
}
}
}
if (globalRotation.first) {
_globalRotationState.velocity.set(globalRotation.second, deltaTime);
}
else {
_globalRotationState.velocity.decelerate(deltaTime);
}
if (zoom.first) {
_truckMovementState.velocity.set(glm::dvec2(zoom.second), deltaTime);
}
else {
_truckMovementState.velocity.decelerate(deltaTime);
}
if (localRoll.first) {
_localRollState.velocity.set(localRoll.second, deltaTime);
}
else {
_localRollState.velocity.decelerate(deltaTime);
}
if (globalRoll.first) {
_globalRollState.velocity.set(globalRoll.second, deltaTime);
}
else {
_globalRollState.velocity.decelerate(deltaTime);
}
if (localRotation.first) {
_localRotationState.velocity.set(localRotation.second, deltaTime);
}
else {
_localRotationState.velocity.decelerate(deltaTime);
}
}
void JoystickCameraStates::setAxisMapping(std::string joystickName,
int axis, AxisType mapping,
AxisInvert shouldInvert,
JoystickType joystickType,
bool isSticky,
double sensitivity)
{
JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(joystickName);
if (!joystickCameraState) {
return;
}
// If the axis index is too big for the vector then resize it to have room
if (axis >= joystickCameraState->axisMapping.size()) {
joystickCameraState->axisMapping.resize(axis + 1);
joystickCameraState->prevAxisValues.resize(axis + 1);
}
joystickCameraState->axisMapping[axis].type = mapping;
joystickCameraState->axisMapping[axis].invert = shouldInvert;
joystickCameraState->axisMapping[axis].joystickType = joystickType;
joystickCameraState->axisMapping[axis].isSticky = isSticky;
joystickCameraState->axisMapping[axis].sensitivity = sensitivity;
joystickCameraState->prevAxisValues[axis] =
global::joystickInputStates->axis(joystickName, axis);
}
void JoystickCameraStates::setAxisMappingProperty(std::string joystickName,
int axis,
std::string propertyUri,
float min, float max,
AxisInvert shouldInvert,
bool isRemote)
{
JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(joystickName);
if (!joystickCameraState) {
return;
}
// If the axis index is too big for the vector then resize it to have room
if (axis >= joystickCameraState->axisMapping.size()) {
joystickCameraState->axisMapping.resize(axis + 1);
joystickCameraState->prevAxisValues.resize(axis + 1);
}
joystickCameraState->axisMapping[axis].type = AxisType::Property;
joystickCameraState->axisMapping[axis].invert = shouldInvert;
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* joystick = joystickCameraState(joystickName);
if (!joystick) {
JoystickCameraStates::AxisInformation dummy;
return dummy;
}
if (axis >= joystick->axisMapping.size()) {
JoystickCameraStates::AxisInformation dummy;
return dummy;
}
return joystick->axisMapping[axis];
}
void JoystickCameraStates::setDeadzone(const std::string& joystickName, int axis,
float deadzone)
{
JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(joystickName);
if (!joystickCameraState) {
return;
}
// If the axis index is too big for the vector then resize it to have room
if (axis >= joystickCameraState->axisMapping.size()) {
joystickCameraState->axisMapping.resize(axis + 1);
joystickCameraState->prevAxisValues.resize(axis + 1);
}
joystickCameraState->axisMapping[axis].deadzone = deadzone;
}
float JoystickCameraStates::deadzone(const std::string& joystickName, int axis) const {
const JoystickCameraState* joystick = joystickCameraState(joystickName);
if (!joystick) {
return 0.f;
}
if (axis >= joystick->axisMapping.size()) {
return 0.f;
}
return joystick->axisMapping[axis].deadzone;
}
void JoystickCameraStates::bindButtonCommand(const std::string& joystickName,
int button, std::string command,
JoystickAction action,
ButtonCommandRemote remote,
std::string documentation)
{
JoystickCameraState* joystickCameraState = findOrAddJoystickCameraState(joystickName);
if (!joystickCameraState) {
return;
}
joystickCameraState->buttonMapping.insert({
button,
{ std::move(command), action, remote, std::move(documentation) }
});
}
void JoystickCameraStates::clearButtonCommand(const std::string& joystickName,
int button)
{
JoystickCameraState* joystick = joystickCameraState(joystickName);
if (!joystick) {
return;
}
for (auto it = joystick->buttonMapping.begin();
it != joystick->buttonMapping.end(); )
{
// If the current iterator is the button that we are looking for, delete it
// (std::multimap::erase will return the iterator to the next element for us)
if (it->first == button) {
it = joystick->buttonMapping.erase(it);
}
else {
++it;
}
}
}
std::vector<std::string> JoystickCameraStates::buttonCommand(
const std::string& joystickName,
int button) const
{
std::vector<std::string> result;
const JoystickCameraState* joystick = joystickCameraState(joystickName);
if (!joystick) {
return result;
}
auto itRange = joystick->buttonMapping.equal_range(button);
for (auto it = itRange.first; it != itRange.second; ++it) {
result.push_back(it->second.command);
}
return result;
}
JoystickCameraStates::JoystickCameraState* JoystickCameraStates::joystickCameraState(
const std::string& joystickName)
{
for (JoystickCameraState& joystickCameraState : _joystickCameraStates) {
if (joystickCameraState.joystickName == joystickName) {
return &joystickCameraState;
}
}
return nullptr;
}
const JoystickCameraStates::JoystickCameraState*
JoystickCameraStates::joystickCameraState(const std::string& joystickName) const
{
for (const JoystickCameraState& joystickCameraState : _joystickCameraStates) {
if (joystickCameraState.joystickName == joystickName) {
return &joystickCameraState;
}
}
LWARNING(fmt::format("Cannot find JoystickCameraState with name '{}'", joystickName));
return nullptr;
}
JoystickCameraStates::JoystickCameraState*
JoystickCameraStates::findOrAddJoystickCameraState(const std::string& joystickName)
{
JoystickCameraState* joystick = joystickCameraState(joystickName);
if (!joystick) {
if (_joystickCameraStates.size() < JoystickInputStates::MaxNumJoysticks) {
_joystickCameraStates.push_back(JoystickCameraState());
joystick = &_joystickCameraStates.back();
joystick->joystickName = joystickName;
}
else {
LWARNING(fmt::format("Cannot add more joysticks, only {} joysticks are "
"supported", JoystickInputStates::MaxNumJoysticks));
return nullptr;
}
}
return joystick;
}
} // namespace openspace::interaction