diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a3b226dafc..a1ab6d35ae 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1456,6 +1456,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { void Application::focusOutEvent(QFocusEvent* event) { _keyboardMouseDevice.focusOutEvent(event); + SixenseManager::getInstance().focusOutEvent(); SDL2Manager::getInstance()->focusOutEvent(); // synthesize events for keys currently pressed, since we may not get their release events diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index cf1e6bce47..5d7e51b103 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -82,6 +82,8 @@ void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { case SDL_CONTROLLER_AXIS_TRIGGERLEFT: _axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS; break; + default: + break; } } @@ -102,8 +104,8 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { _deviceID = mapper.getFreeDeviceID(); auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name)); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); }; + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; #ifdef HAVE_SDL2 diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp index aeeb2480e7..fcb60cca26 100755 --- a/interface/src/devices/KeyboardMouseDevice.cpp +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -160,8 +160,8 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { _deviceID = mapper.getFreeDeviceID(); auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard")); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); }; + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) { diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index f1a762e64f..54cf202838 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -38,6 +38,10 @@ typedef int (*SixenseTakeIntFunction)(int); typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*); #endif +// These bits aren't used for buttons, so they can be used as masks: +const unsigned int LEFT_MASK = 0; +const unsigned int RIGHT_MASK = 1U << 1; + #endif SixenseManager& SixenseManager::getInstance() { @@ -147,6 +151,7 @@ void SixenseManager::update(float deltaTime) { #ifdef HAVE_SIXENSE Hand* hand = DependencyManager::get()->getMyAvatar()->getHand(); if (_isInitialized && _isEnabled) { + _buttonPressedMap.clear(); #ifdef __APPLE__ SixenseBaseFunction sixenseGetNumActiveControllers = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); @@ -154,12 +159,18 @@ void SixenseManager::update(float deltaTime) { if (sixenseGetNumActiveControllers() == 0) { _hydrasConnected = false; + if (_deviceID) { + Application::getUserInputMapper()->removeDevice(_deviceID); + _deviceID = 0; + } return; } PerformanceTimer perfTimer("sixense"); if (!_hydrasConnected) { _hydrasConnected = true; + registerToUserInputMapper(*Application::getUserInputMapper()); + getInstance().assignDefaultInputMapping(*Application::getUserInputMapper()); UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); } @@ -216,12 +227,14 @@ void SixenseManager::update(float deltaTime) { palm->setActive(false); // if this isn't a Sixsense ID palm, always make it inactive } - // Read controller buttons and joystick into the hand palm->setControllerButtons(data->buttons); palm->setTrigger(data->trigger); palm->setJoystick(data->joystick_x, data->joystick_y); + handleButtonEvent(data->buttons, numActiveControllers - 1); + handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); + // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput) && !_controllersAtBase) { emulateMouse(palm, numActiveControllers - 1); @@ -590,3 +603,137 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { #endif // HAVE_SIXENSE +void SixenseManager::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; + +void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) { + _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0) ? stickY : 0.0f; + _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0) ? -stickY : 0.0f; + _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0) ? stickX : 0.0f; + _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0) ? -stickX : 0.0f; + _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger; +} + +void SixenseManager::handleButtonEvent(unsigned int buttons, int index) { + if (buttons & BUTTON_0) { + _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel()); + } + if (buttons & BUTTON_1) { + _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel()); + } + if (buttons & BUTTON_2) { + _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel()); + } + if (buttons & BUTTON_3) { + _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel()); + } + if (buttons & BUTTON_4) { + _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel()); + } + if (buttons & BUTTON_FWD) { + _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel()); + } +} + +void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getFreeDeviceID(); + + auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Hydra")); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 0), "Left Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 0), "Left Button 1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Stick Left")); + return availableInputs; + }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; + mapper.registerDevice(_deviceID, proxy); +} + +void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BUTTON_MOVE_SPEED = 1.0f; + const float BOOM_SPEED = .1f; + + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED); + + // Buttons + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED); + + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED); +} + +float SixenseManager::getButton(int channel) const { + if (!_buttonPressedMap.empty()) { + if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { + return 1.0f; + } else { + return 0.0f; + } + } + return 0.0f; +} + +float SixenseManager::getAxis(int channel) const { + auto axis = _axisStateMap.find(channel); + if (axis != _axisStateMap.end()) { + return (*axis).second; + } else { + return 0.0f; + } +} + +UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) { + return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input SixenseManager::makeInput(SixenseManager::JoystickAxisChannel axis, int index) { + return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS); +} diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 6f47f2fac9..2ca6b0fb5b 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -13,6 +13,7 @@ #define hifi_SixenseManager_h #include +#include #ifdef HAVE_SIXENSE #include @@ -25,6 +26,8 @@ #endif +#include "ui/UserInputMapper.h" + class PalmData; const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 @@ -45,6 +48,14 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; class SixenseManager : public QObject { Q_OBJECT public: + enum JoystickAxisChannel { + AXIS_Y_POS = 1U << 0, + AXIS_Y_NEG = 1U << 3, + AXIS_X_POS = 1U << 4, + AXIS_X_NEG = 1U << 5, + BACK_TRIGGER = 1U << 6, + }; + static SixenseManager& getInstance(); void initialize(); @@ -60,6 +71,21 @@ public: bool getInvertButtons() const { return _invertButtons; } void setInvertButtons(bool invertSixenseButtons) { _invertButtons = invertSixenseButtons; } + typedef std::unordered_set ButtonPressedMap; + typedef std::map AxisStateMap; + + float getButton(int channel) const; + float getAxis(int channel) const; + + UserInputMapper::Input makeInput(unsigned int button, int index); + UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index); + + void registerToUserInputMapper(UserInputMapper& mapper); + void assignDefaultInputMapping(UserInputMapper& mapper); + + void update(); + void focusOutEvent(); + public slots: void toggleSixense(bool shouldEnable); void setFilter(bool filter); @@ -70,6 +96,8 @@ private: ~SixenseManager(); #ifdef HAVE_SIXENSE + void handleButtonEvent(unsigned int buttons, int index); + void handleAxisEvent(float x, float y, float trigger, int index); void updateCalibration(const sixenseControllerData* controllers); void emulateMouse(PalmData* palm, int index); @@ -110,6 +138,12 @@ private: float _reticleMoveSpeed = DEFAULT_SIXENSE_RETICLE_MOVE_SPEED; bool _invertButtons = DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS; + +protected: + int _deviceID = 0; + + ButtonPressedMap _buttonPressedMap; + AxisStateMap _axisStateMap; }; #endif // hifi_SixenseManager_h