From d75353eeb2355c13fce7b8ccb1991ea820844559 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 7 Jul 2015 14:35:55 -0700 Subject: [PATCH] added left/right hand click actions, vive controller should be able to emulate mouse events --- interface/src/Application.cpp | 132 +++++++++++++++++- interface/src/Application.h | 8 +- interface/src/Menu.cpp | 4 +- interface/src/Menu.h | 4 +- interface/src/devices/SixenseManager.cpp | 4 +- interface/src/ui/ApplicationCompositor.cpp | 4 +- .../src/input-plugins/UserInputMapper.cpp | 4 + .../src/input-plugins/UserInputMapper.h | 5 +- .../input-plugins/ViveControllerManager.cpp | 16 ++- tests/ui/src/main.cpp | 4 +- 10 files changed, 168 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cad583bd21..bcc5d0cb81 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -671,6 +671,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : #endif ViveControllerManager::getInstance().activate(); + + _oldHandMouseX[0] = -1; + _oldHandMouseY[0] = -1; + _oldHandMouseX[1] = -1; + _oldHandMouseY[1] = -1; + _oldHandLeftClick[0] = false; + _oldHandRightClick[0] = false; + _oldHandLeftClick[1] = false; + _oldHandRightClick[1] = false; auto applicationUpdater = DependencyManager::get(); connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); @@ -2622,6 +2631,12 @@ void Application::update(float deltaTime) { Hand* hand = DependencyManager::get()->getMyAvatar()->getHand(); setPalmData(hand, leftHand, LEFT_HAND_INDEX); setPalmData(hand, rightHand, RIGHT_HAND_INDEX); + if (Menu::getInstance()->isOptionChecked(MenuOption::HandMouseInput)) { + emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK), + userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX); + emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK), + userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX); + } } _myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN)); _myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT)); @@ -2802,6 +2817,121 @@ void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, int i palm->setRawRotation(pose.getRotation()); } +void Application::emulateMouse(Hand* hand, float click, float shift, int index) { + // Locate the palm, if it exists and is active + PalmData* palm; + bool foundHand = false; + for (size_t j = 0; j < hand->getNumPalms(); j++) { + if (hand->getPalms()[j].getSixenseID() == index) { + palm = &(hand->getPalms()[j]); + foundHand = true; + } + } + if (!foundHand || !palm->isActive()) { + return; + } + + // Process the mouse events + QPoint pos; + + unsigned int deviceID = index == 0 ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT; + + if (Menu::getInstance()->isOptionChecked(MenuOption::HandLasers) || qApp->isHMDMode()) { + pos = qApp->getApplicationCompositor().getPalmClickLocation(palm); + } + else { + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(_myAvatar->getOrientation()) * palm->getFingerDirection(); + + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2); + float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); + auto canvasSize = qApp->getCanvasSize(); + // Get the pixel range over which the xAngle and yAngle are scaled + // TODO: move this from SixenseManager + float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); + + pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle); + pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle); + + } + + //If we are off screen then we should stop processing, and if a trigger or bumper is pressed, + //we should unpress them. + if (pos.x() == INT_MAX) { + if (_oldHandLeftClick[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); + + qApp->mouseReleaseEvent(&mouseEvent, deviceID); + + _oldHandLeftClick[index] = false; + } + if (_oldHandRightClick[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); + + qApp->mouseReleaseEvent(&mouseEvent, deviceID); + + _oldHandRightClick[index] = false; + } + return; + } + + //If position has changed, emit a mouse move to the application + if (pos.x() != _oldHandMouseX[index] || pos.y() != _oldHandMouseY[index]) { + QMouseEvent mouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::NoButton, 0); + + // Only send the mouse event if the opposite left button isnt held down. + // Is this check necessary? + if (!_oldHandLeftClick[(int)(!index)]) { + qApp->mouseMoveEvent(&mouseEvent, deviceID); + } + } + _oldHandMouseX[index] = pos.x(); + _oldHandMouseY[index] = pos.y(); + + //We need separate coordinates for clicks, since we need to check if + //a magnification window was clicked on + int clickX = pos.x(); + int clickY = pos.y(); + //Set pos to the new click location, which may be the same if no magnification window is open + pos.setX(clickX); + pos.setY(clickY); + + // Right click + if (shift == 1.0f && click == 1.0f) { + if (!_oldHandRightClick[index]) { + _oldHandRightClick[index] = true; + + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::RightButton, Qt::RightButton, 0); + + qApp->mousePressEvent(&mouseEvent, deviceID); + } + } else if (_oldHandRightClick[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); + + qApp->mouseReleaseEvent(&mouseEvent, deviceID); + + _oldHandRightClick[index] = false; + } + + // Left click + if (shift != 1.0f && click == 1.0f) { + if (!_oldHandLeftClick[index]) { + _oldHandLeftClick[index] = true; + + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, 0); + + qApp->mousePressEvent(&mouseEvent, deviceID); + } + } else if (_oldHandLeftClick[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); + + qApp->mouseReleaseEvent(&mouseEvent, deviceID); + + _oldHandLeftClick[index] = false; + } +} + int Application::sendNackPackets() { if (Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { @@ -3683,7 +3813,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems); } //Render the sixense lasers - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::HandLasers)) { _myAvatar->renderLaserPointers(*renderArgs->_batch); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 5aa6cd7eff..29edfe766a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -482,8 +482,9 @@ private: void cleanupBeforeQuit(); void update(float deltaTime); - + void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, int index); + void emulateMouse(Hand* hand, float click, float shift, int index); // Various helper functions called during update() void updateLOD(); @@ -671,6 +672,11 @@ private: glm::vec3 _headPosition; glm::quat _headOrientation; + + int _oldHandMouseX[2]; + int _oldHandMouseY[2]; + bool _oldHandLeftClick[2]; + bool _oldHandRightClick[2]; }; #endif // hifi_Application_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8ee3d34dd2..f8aabcad12 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -466,6 +466,8 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandMouseInput, 0, true); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandLasers, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false); #if 0 @@ -489,8 +491,6 @@ Menu::Menu() { true, qApp, SLOT(setLowVelocityFilter(bool))); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false); #endif MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 2eb269077a..55a3538125 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -199,6 +199,8 @@ namespace MenuOption { const QString FrameTimer = "Show Timer"; const QString FullscreenMirror = "Fullscreen Mirror"; const QString GlowWhenSpeaking = "Glow When Speaking"; + const QString HandMouseInput = "Enable Hand Mouse Input"; + const QString HandLasers = "Enable Hand UI Lasers"; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString IndependentMode = "Independent Mode"; const QString KeyboardMotorControl = "Enable Keyboard Motor Control"; @@ -270,8 +272,6 @@ namespace MenuOption { const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats"; const QString SimpleShadows = "Simple"; const QString SixenseEnabled = "Enable Hydra Support"; - const QString SixenseMouseInput = "Enable Sixense Mouse Input"; - const QString SixenseLasers = "Enable Sixense UI Lasers"; const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations"; const QString Stars = "Stars"; const QString Stats = "Stats"; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index a8e45914dd..607cacdc40 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -248,7 +248,7 @@ void SixenseManager::update(float deltaTime) { 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) { + if (Menu::getInstance()->isOptionChecked(MenuOption::HandMouseInput) && !_controllersAtBase) { emulateMouse(palm, numActiveControllers - 1); } @@ -518,7 +518,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { triggerButton = Qt::LeftButton; } - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers) || qApp->isHMDMode()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::HandLasers) || qApp->isHMDMode()) { pos = qApp->getApplicationCompositor().getPalmClickLocation(palm); } else { // Get directon relative to avatar orientation diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 7221c9b693..e50d10b772 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -415,7 +415,7 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) { _magActive[MOUSE] = _magnifier; _reticleActive[LEFT_CONTROLLER] = false; _reticleActive[RIGHT_CONTROLLER] = false; - } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::HandMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer _reticleActive[MOUSE] = false; _magActive[MOUSE] = false; @@ -490,7 +490,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { auto canvasSize = qApp->getCanvasSize(); int mouseX, mouseY; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::HandLasers)) { QPoint res = getPalmClickLocation(palmData); mouseX = res.x(); mouseY = res.y(); diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index efffa873c6..b8aa52a508 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -248,6 +248,8 @@ void UserInputMapper::assignDefaulActionScales() { _actionScales[BOOM_OUT] = 0.5f; // .5m per unit _actionScales[LEFT_HAND] = 1.0f; // default _actionScales[RIGHT_HAND] = 1.0f; // default + _actionScales[LEFT_HAND_CLICK] = 1.0f; // on + _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on _actionStates[SHIFT] = 1.0f; // on _actionStates[ACTION1] = 1.0f; // default _actionStates[ACTION2] = 1.0f; // default @@ -270,6 +272,8 @@ void UserInputMapper::createActionNames() { _actionNames[BOOM_OUT] = "BOOM_OUT"; _actionNames[LEFT_HAND] = "LEFT_HAND"; _actionNames[RIGHT_HAND] = "RIGHT_HAND"; + _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; + _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; _actionNames[SHIFT] = "SHIFT"; _actionNames[ACTION1] = "ACTION1"; _actionNames[ACTION2] = "ACTION2"; diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/input-plugins/src/input-plugins/UserInputMapper.h index 639d2bb132..5f17aa0801 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.h @@ -152,7 +152,10 @@ public: LEFT_HAND, RIGHT_HAND, - + + LEFT_HAND_CLICK, + RIGHT_HAND_CLICK, + SHIFT, ACTION1, diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index c4c5513092..79735c8cec 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -31,10 +31,12 @@ const unsigned int RIGHT_MASK = 1U; const uint64_t VR_BUTTON_A = 1U << 1; const uint64_t VR_GRIP_BUTTON = 1U << 2; const uint64_t VR_TRACKPAD_BUTTON = 1ULL << 32; +const uint64_t VR_TRIGGER_BUTTON = 1ULL << 33; const unsigned int BUTTON_A = 1U << 1; const unsigned int GRIP_BUTTON = 1U << 2; const unsigned int TRACKPAD_BUTTON = 1U << 3; +const unsigned int TRIGGER_BUTTON = 1U << 4; ViveControllerManager& ViveControllerManager::getInstance() { static ViveControllerManager sharedInstance; @@ -111,11 +113,9 @@ void ViveControllerManager::update() { if (_deviceID != 0) { userInputMapper->removeDevice(_deviceID); _deviceID = 0; - _poseStateMap[makeInput(LEFT_HAND).getChannel()] = UserInputMapper::PoseValue(); - _poseStateMap[makeInput(RIGHT_HAND).getChannel()] = UserInputMapper::PoseValue(); + //_poseStateMap[makeInput(LEFT_HAND).getChannel()] = UserInputMapper::PoseValue(); + //_poseStateMap[makeInput(RIGHT_HAND).getChannel()] = UserInputMapper::PoseValue(); } - _trackedControllers = numTrackedControllers; - return; } if (_trackedControllers == 0 && numTrackedControllers > 0) { @@ -154,6 +154,9 @@ void ViveControllerManager::handleButtonEvent(uint64_t buttons, int index) { if (buttons & VR_TRACKPAD_BUTTON) { _buttonPressedMap.insert(makeInput(TRACKPAD_BUTTON, index).getChannel()); } + if (buttons & VR_TRIGGER_BUTTON) { + _buttonPressedMap.insert(makeInput(TRIGGER_BUTTON, index).getChannel()); + } } void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { @@ -186,6 +189,7 @@ void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) { availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 0), "Left Button A")); availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); + availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down")); @@ -196,6 +200,7 @@ void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) { availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 1), "Right Button A")); availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button")); availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button")); + availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down")); @@ -235,6 +240,9 @@ void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_A, 0)); mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_A, 1)); + + mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(TRIGGER_BUTTON, 0)); + mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(TRIGGER_BUTTON, 1)); // Hands mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 19070e9699..68ffa904c4 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -148,6 +148,8 @@ public: GlowWhenSpeaking, NamesAboveHeads, GoToUser, + HandMouseInput, + HandLasers, HMDTools, IncreaseAvatarSize, KeyboardMotorControl, @@ -214,8 +216,6 @@ public: ShowIKConstraints, SimpleShadows, SixenseEnabled, - SixenseMouseInput, - SixenseLasers, ShiftHipsForIdleAnimations, Stars, Stats,