diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js new file mode 100644 index 0000000000..fa8cf48b9b --- /dev/null +++ b/examples/tests/controllerInterfaceTest.js @@ -0,0 +1,30 @@ +ControllerTest = function() { + + print("Actions"); + for (var prop in Controller.Actions) { + print("\t" + prop); + } + print("Standard"); + for (var prop in Controller.Standard) { + print("\t" + prop); + } + print("Hardware"); + for (var prop in Controller.Hardware) { + print("\t" + prop); + for (var prop2 in Controller.Hardware[prop]) { + print("\t\t" + prop2); + } + } + print("Done"); + + var that = this; + Script.scriptEnding.connect(function() { + that.onCleanup(); + }); +} + +ControllerTest.prototype.onCleanup = function() { +} + + +new ControllerTest(); \ No newline at end of file diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json new file mode 100644 index 0000000000..25c8db61cb --- /dev/null +++ b/interface/resources/controllers/hydra.json @@ -0,0 +1,28 @@ +{ + "name": "Hydra to Standard", + "channels": [ + { "from": "Hydra.LY", "to": "Standard.LY" }, + { "from": "Hydra.LX", "to": "Standard.LX" }, + { "from": "Hydra.LT", "to": "Standard.LT" }, + { "from": "Hydra.RY", "to": "Standard.RY" }, + { "from": "Hydra.RX", "to": "Standard.RX" }, + { "from": "Hydra.RT", "to": "Standard.RT" }, + + { "from": "Hydra.LB", "to": "Standard.LB" }, + { "from": "Hydra.LS", "to": "Standard.LS" }, + { "from": "Hydra.RB", "to": "Standard.RB" }, + { "from": "Hydra.RS", "to": "Standard.RS" }, + + { "from": "Hydra.L0", "to": "Standard.Back" }, + { "from": "Hydra.L1", "to": "Standard.DL" }, + { "from": "Hydra.L2", "to": "Standard.DD" }, + { "from": "Hydra.L3", "to": "Standard.DR" }, + { "from": "Hydra.L4", "to": "Standard.DU" }, + + { "from": "Hydra.R0", "to": "Standard.Start" }, + { "from": "Hydra.R1", "to": "Standard.X" }, + { "from": "Hydra.R2", "to": "Standard.A" }, + { "from": "Hydra.R3", "to": "Standard.B" }, + { "from": "Hydra.R4", "to": "Standard.Y" } + ] +} diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json new file mode 100644 index 0000000000..bf96320707 --- /dev/null +++ b/interface/resources/controllers/xbox.json @@ -0,0 +1,29 @@ +{ + "name": "XBox to Standard", + "channels": [ + { "from": "XBox.LY", "to": "Standard.LY" }, + { "from": "XBox.LX", "to": "Standard.LX" }, + { "from": "XBox.LT", "to": "Standard.LT" }, + { "from": "XBox.LB", "to": "Standard.LB" }, + { "from": "XBox.LS", "to": "Standard.LS" }, + + { "from": "XBox.RY", "to": "Standard.RY" }, + { "from": "XBox.RX", "to": "Standard.RX" }, + { "from": "XBox.RT", "to": "Standard.RT" }, + { "from": "XBox.RB", "to": "Standard.RB" }, + { "from": "XBox.RS", "to": "Standard.RS" }, + + { "from": "XBox.Back", "to": "Standard.Back" }, + { "from": "XBox.Start", "to": "Standard.Start" }, + + { "from": "XBox.DU", "to": "Standard.DU" }, + { "from": "XBox.DD", "to": "Standard.DD" }, + { "from": "XBox.DL", "to": "Standard.DL" }, + { "from": "XBox.DR", "to": "Standard.DR" }, + + { "from": "XBox.A", "to": "Standard.A" }, + { "from": "XBox.B", "to": "Standard.B" }, + { "from": "XBox.X", "to": "Standard.X" }, + { "from": "XBox.Y", "to": "Standard.Y" } + ] +} diff --git a/libraries/controllers/src/controllers/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp index 351d5b6d1d..4b2376d32a 100644 --- a/libraries/controllers/src/controllers/InputDevice.cpp +++ b/libraries/controllers/src/controllers/InputDevice.cpp @@ -57,3 +57,28 @@ UserInputMapper::PoseValue InputDevice::getPose(int channel) const { return UserInputMapper::PoseValue(); } } + +UserInputMapper::Input InputDevice::makeInput(controller::StandardButtonChannel button) { + return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input InputDevice::makeInput(controller::StandardAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + +UserInputMapper::Input InputDevice::makeInput(controller::StandardPoseChannel pose) { + return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); +} + +UserInputMapper::InputPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) { + return UserInputMapper::InputPair(makeInput(button), name); +} + +UserInputMapper::InputPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) { + return UserInputMapper::InputPair(makeInput(axis), name); +} + +UserInputMapper::InputPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) { + return UserInputMapper::InputPair(makeInput(pose), name); +} + diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 4dbb141832..66f7addc58 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -11,6 +11,7 @@ #pragma once #include "UserInputMapper.h" +#include "StandardControls.h" // Event types for each controller const unsigned int CONTROLLER_0_EVENT = 1500U; @@ -33,7 +34,7 @@ public: UserInputMapper::PoseValue getPose(int channel) const; virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) {}; // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas @@ -49,10 +50,17 @@ public: static bool getLowVelocityFilter() { return _lowVelocityFilter; }; + UserInputMapper::Input makeInput(controller::StandardButtonChannel button); + UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); + UserInputMapper::Input makeInput(controller::StandardPoseChannel pose); + UserInputMapper::InputPair makePair(controller::StandardButtonChannel button, const QString& name); + UserInputMapper::InputPair makePair(controller::StandardAxisChannel button, const QString& name); + UserInputMapper::InputPair makePair(controller::StandardPoseChannel button, const QString& name); public slots: static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; }; protected: + int _deviceID = 0; QString _name; diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index e7a0124deb..aa5bbbba07 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -143,22 +143,3 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { mapper.registerDevice(_deviceID, proxy); } -void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { -#ifdef HAVE_SDL2 - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - -#endif -} - -UserInputMapper::Input Joystick::makeInput(controller::StandardButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); -} - -UserInputMapper::Input Joystick::makeInput(controller::StandardAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); -} - diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index c6537acafe..8e4cdb365f 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -37,16 +37,12 @@ public: // Device functions virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; Joystick() : InputDevice("Joystick") {} ~Joystick(); - UserInputMapper::Input makeInput(controller::StandardButtonChannel button); - UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); - #ifdef HAVE_SDL2 Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); #endif diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index a99e04ff13..9d3d06ecb7 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -123,6 +123,8 @@ void SixenseManager::activate() { loadSettings(); sixenseInit(); _activated = true; + auto userInputMapper = DependencyManager::get(); + registerToUserInputMapper(*userInputMapper); #endif } @@ -176,23 +178,13 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { auto userInputMapper = DependencyManager::get(); if (sixenseGetNumActiveControllers() == 0) { - if (_hydrasConnected) { - qCDebug(inputplugins, "hydra disconnected"); - } - _hydrasConnected = false; - if (_deviceID != 0) { - userInputMapper->removeDevice(_deviceID); - _deviceID = 0; - _poseStateMap.clear(); - } + _poseStateMap.clear(); return; } PerformanceTimer perfTimer("sixense"); if (!_hydrasConnected) { _hydrasConnected = true; - registerToUserInputMapper(*userInputMapper); - assignDefaultInputMapping(*userInputMapper); UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); } @@ -226,12 +218,15 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); position *= METERS_PER_MILLIMETER; - + bool left = i == 0; + using namespace controller; // Check to see if this hand/controller is on the base const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) { - handleButtonEvent(data->buttons, numActiveControllers - 1); - handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); + handleButtonEvent(data->buttons, left); + _axisStateMap[left ? LX : RX] = data->joystick_x; + _axisStateMap[left ? LY : RY] = data->joystick_y; + _axisStateMap[left ? LT : RT] = data->trigger; if (!jointsCaptured) { // Rotation of Palm @@ -241,13 +236,8 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { _poseStateMap.clear(); } } else { - _poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue(); + _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = UserInputMapper::PoseValue(); } - -// // Read controller buttons and joystick into the hand -// palm->setControllerButtons(data->buttons); -// palm->setTrigger(data->trigger); -// palm->setJoystick(data->joystick_x, data->joystick_y); } if (numActiveControllers == 2) { @@ -367,39 +357,35 @@ void SixenseManager::focusOutEvent() { _buttonPressedMap.clear(); }; -void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) { - _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f; - _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f; - _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f; - _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f; - _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger; +void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { } -void SixenseManager::handleButtonEvent(unsigned int buttons, int index) { +void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { + using namespace controller; if (buttons & BUTTON_0) { - _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel()); + _buttonPressedMap.insert(left ? BACK : START); } if (buttons & BUTTON_1) { - _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel()); + _buttonPressedMap.insert(left ? DL : X); } if (buttons & BUTTON_2) { - _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel()); + _buttonPressedMap.insert(left ? DD : A); } if (buttons & BUTTON_3) { - _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel()); + _buttonPressedMap.insert(left ? DR : B); } if (buttons & BUTTON_4) { - _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel()); + _buttonPressedMap.insert(left ? DU : Y); } if (buttons & BUTTON_FWD) { - _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel()); + _buttonPressedMap.insert(left ? LB : RB); } if (buttons & BUTTON_TRIGGER) { - _buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel()); + _buttonPressedMap.insert(left ? LS : RS); } } -void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) { +void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left) { #ifdef HAVE_SIXENSE // From ABOVE the sixense coordinate frame looks like this: // @@ -443,7 +429,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers // and how they fit into the hand in their relaxed state. This offset is a quarter turn about // the sixense's z-axis, with its direction different for the two hands: - float sign = (index == 0) ? 1.0f : -1.0f; + float sign = left ? 1.0f : -1.0f; const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z); // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation @@ -458,104 +444,46 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int // TODO: find a shortcut with fewer rotations. rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; - _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); + _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = + UserInputMapper::PoseValue(position, rotation); #endif // HAVE_SIXENSE } void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { // Grab the current free device ID _deviceID = mapper.getFreeDeviceID(); - auto proxy = std::make_shared(_name); 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->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { + using namespace controller; + 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_TRIGGER, 0), "Left Trigger Press")); - - 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")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 1), "Right Trigger Press")); - + availableInputs.append(UserInputMapper::InputPair(makeInput(BACK), "L0")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DL), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DD), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DR), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DU), "L4")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(START), "R0")); + availableInputs.append(UserInputMapper::InputPair(makeInput(X), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(A), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(B), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Y), "R4")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RB), "RB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RS), "RS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RX), "RX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RT), "RT")); 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 = 0.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); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0)); - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1)); - - mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); - - mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); - - // TODO find a mechanism to allow users to navigate the context menu via - mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(BUTTON_0, 0)); - mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(BUTTON_0, 1)); - -} - // virtual void SixenseManager::saveSettings() const { Settings settings; @@ -580,15 +508,3 @@ void SixenseManager::loadSettings() { } settings.endGroup(); } - -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); -} - -UserInputMapper::Input SixenseManager::makeInput(JointChannel joint) { - return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE); -} diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 4558f5d268..2e7dd3223d 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -25,6 +25,7 @@ #endif #include +#include #include "InputPlugin.h" @@ -44,19 +45,6 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; class SixenseManager : public InputPlugin, public InputDevice { 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, - }; - - enum JointChannel { - LEFT_HAND = 0, - RIGHT_HAND, - }; - SixenseManager(); // Plugin functions @@ -73,14 +61,9 @@ public: // Device functions virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; - UserInputMapper::Input makeInput(unsigned int button, int index); - UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index); - UserInputMapper::Input makeInput(JointChannel joint); - virtual void saveSettings() const override; virtual void loadSettings() override; @@ -88,9 +71,9 @@ public slots: void setSixenseFilter(bool filter); private: - void handleButtonEvent(unsigned int buttons, int index); - void handleAxisEvent(float x, float y, float trigger, int index); - void handlePoseEvent(glm::vec3 position, glm::quat rotation, int index); + void handleButtonEvent(unsigned int buttons, bool left); + void handleAxisEvent(float x, float y, float trigger, bool left); + void handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left); void updateCalibration(void* controllers); diff --git a/tests/controllers/qml/Hydra.qml b/tests/controllers/qml/Hydra.qml new file mode 100644 index 0000000000..5ef47c4d83 --- /dev/null +++ b/tests/controllers/qml/Hydra.qml @@ -0,0 +1,35 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./hydra" +import "./controls" + +Item { + id: root + width: 480 + height: width * 3.0 / 4.0 + property var device + property real scale: width / 480 + property real rightOffset: (width / 2) * scale + + Image { + anchors.fill: parent + source: "hydra/hydra.png" + + HydraStick { + leftStick: true + scale: root.scale + device: root.device + } + + + HydraStick { + leftStick: false + scale: root.scale + device: root.device + } + + } +} diff --git a/tests/controllers/qml/Xbox.qml b/tests/controllers/qml/Xbox.qml index ae66081162..bc9acd5a43 100644 --- a/tests/controllers/qml/Xbox.qml +++ b/tests/controllers/qml/Xbox.qml @@ -8,14 +8,20 @@ import "./controls" Item { id: root - + property real aspect: 300.0 / 215.0 + width: 300 + height: width / aspect property var device - - property real scale: 1.0 - width: 300 * scale - height: 215 * scale + property string label: "" + property real scale: width / 300.0 Image { + Text { + anchors.left: parent.left + anchors.top: parent.top + text: root.label + visible: root.label != "" + } anchors.fill: parent source: "xbox/xbox360-controller-md.png" diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index 5f9bbd455e..4beda82df3 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -10,16 +10,24 @@ Column { id: root property var actions: Controllers.Actions property var standard: Controllers.Standard + property var hydra: null property var testMapping: null property var xbox: null Component.onCompleted: { - var patt = /^X360Controller/; + var xboxRegex = /^X360Controller/; + var hydraRegex = /^Hydra/; for (var prop in Controllers.Hardware) { - if(patt.test(prop)) { + if(xboxRegex.test(prop)) { root.xbox = Controllers.Hardware[prop] - break + print("found xbox") + continue + } + if (hydraRegex.test(prop)) { + root.hydra = Controllers.Hardware[prop] + print("found hydra") + continue } } } @@ -79,23 +87,15 @@ Column { mapping.join(standard.LB, standard.RB).to(actions.Yaw); mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); - // mapping.modifier(keyboard.Ctrl).scale(2.0) - // mapping.from(keyboard.A).to(actions.TranslateLeft) // mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) // mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) - // // First loopbacks // // Then non-loopbacks by constraint level (number of inputs) // mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) - // mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) - - // mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) - - // mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) testMapping = mapping; enabled = false @@ -114,12 +114,19 @@ Column { } } + Row { + Xbox { device: root.standard; label: "Standard"; width: 360 } + } + Row { spacing: 8 - Xbox { device: root.xbox } - Xbox { device: root.standard } + Xbox { device: root.xbox; label: "XBox"; width: 360 } } + Row { + spacing: 8 + Hydra { device: root.hydra; width: 360 } + } Row { spacing: 8 diff --git a/tests/controllers/qml/controls/AnalogStick.qml b/tests/controllers/qml/controls/AnalogStick.qml index 5d011411c9..4e8ceb5736 100644 --- a/tests/controllers/qml/controls/AnalogStick.qml +++ b/tests/controllers/qml/controls/AnalogStick.qml @@ -8,6 +8,8 @@ Item { property int size: 64 width: size height: size + property bool invertY: false + property int halfSize: size / 2 property var controlIds: [ 0, 0 ] @@ -18,6 +20,9 @@ Item { Controllers.getValue(controlIds[0]), Controllers.getValue(controlIds[1]) ); + if (root.invertY) { + value.y = value.y * -1.0 + } canvas.requestPaint(); } diff --git a/tests/controllers/qml/hydra/HydraButtons.qml b/tests/controllers/qml/hydra/HydraButtons.qml new file mode 100644 index 0000000000..6c3070d2b1 --- /dev/null +++ b/tests/controllers/qml/hydra/HydraButtons.qml @@ -0,0 +1,18 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + width: 72 * scale + height: 48 * scale + property var device + property real scale: 1.0 + property bool leftStick: true + +} + + diff --git a/tests/controllers/qml/hydra/HydraStick.qml b/tests/controllers/qml/hydra/HydraStick.qml new file mode 100644 index 0000000000..3c22789f6d --- /dev/null +++ b/tests/controllers/qml/hydra/HydraStick.qml @@ -0,0 +1,91 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property var device + property real scale: 1.0 + property bool leftStick: true + width: parent.width / 2; height: parent.height + x: leftStick ? 0 : parent.width / 2 + + Text { + x: parent.width / 2 - width / 2; y: parent.height / 2 - height / 2 + text: root.leftStick ? "L" : "R" + color: 'green' + } + + // Analog Stick + AnalogStick { + size: 64 * root.scale + x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; z: 100 + invertY: true + controlIds: [ + root.leftStick ? root.device.LX : root.device.RX, + root.leftStick ? root.device.LY : root.device.RY + ] + } + + // Stick press + ToggleButton { + controlId: root.leftStick ? root.device.LS : root.device.RS + width: 16 * root.scale; height: 16 * root.scale + x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; + color: 'yellow' + } + + // Trigger + AnalogButton { + controlId: root.leftStick ? root.device.LT : root.device.RT + width: 8 * root.scale ; height: 64 * root.scale + y: 24 * root.scale + x: root.leftStick ? (48 * root.scale) : root.width - (48 * root.scale) - width / 2 + } + + // Bumper + ToggleButton { + controlId: root.leftStick ? root.device.LB : root.device.RB + height: 16 * root.scale; width: 32 * root.scale + x: 128 * root.scale - width / 2; y: 24 * root.scale + color: 'red' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L0 : root.device.R0 + height: 16 * root.scale; width: 4 * root.scale + x: 128 * root.scale - width / 2; y: 109 * root.scale + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L1 : root.device.R1 + width: 16 * root.scale; height: 16 * root.scale + x: 103 * root.scale - width / 2; y: 100 * root.scale - height / 2 + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L2 : root.device.R2 + width: 16 * root.scale; height: 16 * root.scale + x: 148 * root.scale - width / 2; y: 100 * root.scale - height / 2 + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L3 : root.device.R3 + width: 16 * root.scale; height: 16 * root.scale + x: 97 * root.scale - width / 2; y: 76 * root.scale - height / 2 + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L4 : root.device.R4 + width: 16 * root.scale; height: 16 * root.scale + x: 155 * root.scale - width / 2; y: 76 * root.scale - height / 2 + color: 'yellow' + } +} diff --git a/tests/controllers/qml/hydra/hydra.png b/tests/controllers/qml/hydra/hydra.png new file mode 100644 index 0000000000..a7549ab231 Binary files /dev/null and b/tests/controllers/qml/hydra/hydra.png differ diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index e173b7893f..0c5ef09867 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -88,14 +88,9 @@ public: int main(int argc, char** argv) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - for (auto path : qApp->libraryPaths()) { - qDebug() << path; - } - - for (auto path : qApp->libraryPaths()) { - qDebug() << path; - } + new PluginContainerProxy(); + // Simulate our application idle loop QTimer timer; QObject::connect(&timer, &QTimer::timeout, [] { static float last = secTimestampNow(); @@ -122,11 +117,12 @@ int main(int argc, char** argv) { auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); } + inputPlugin->pluginUpdate(0, false); } - //new PluginContainerProxy(); auto rootContext = engine.rootContext(); rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); } + engine.load(getQmlDir() + "main.qml"); app.exec(); return 0;