mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Fix for vive controllers sometimes not working
* Fixed bug with input devices that where added, removed then re-added. The default mappings were being ignored on the second add. * Fixed potential crash when hardware inputPlugin device poses were polled from the JavaScript thread by taking the UserInputManager lock during pluginUpdate. * Renamed Controller.Hardware.Vive.LB & RB to LeftGrip and RightGrip, to better match Oculus touch. * Updated resource/controller/vive.json to reflect this new mapping. * Exposed touch pad capacitive touch events to JavaScript as Controller.Hardware.Vive.LSTouch and RSTouch. * Added viveTouchpadTest.js script to test LSTouch and RSTouch events.
This commit is contained in:
parent
59c6a6dfea
commit
09a4e0eaa8
9 changed files with 172 additions and 34 deletions
|
@ -5,14 +5,14 @@
|
|||
{ "from": "Vive.LX", "when": "Vive.LS", "to": "Standard.LX" },
|
||||
|
||||
{ "from": "Vive.LT", "to": "Standard.LT" },
|
||||
{ "from": "Vive.LB", "to": "Standard.LB" },
|
||||
{ "from": "Vive.LeftGrip", "to": "Standard.LB" },
|
||||
{ "from": "Vive.LS", "to": "Standard.LS" },
|
||||
|
||||
{ "from": "Vive.RY", "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" },
|
||||
{ "from": "Vive.RX", "when": "Vive.RS", "to": "Standard.RX" },
|
||||
|
||||
{ "from": "Vive.RT", "to": "Standard.RT" },
|
||||
{ "from": "Vive.RB", "to": "Standard.RB" },
|
||||
{ "from": "Vive.RightGrip", "to": "Standard.RB" },
|
||||
{ "from": "Vive.RS", "to": "Standard.RS" },
|
||||
|
||||
{ "from": "Vive.LeftApplicationMenu", "to": "Standard.Back" },
|
||||
|
|
|
@ -58,7 +58,7 @@ controller::UserInputMapper::UserInputMapper() {
|
|||
|
||||
namespace controller {
|
||||
|
||||
|
||||
|
||||
UserInputMapper::~UserInputMapper() {
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
|
|||
recordDeviceOfType(device->getName());
|
||||
|
||||
qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID;
|
||||
|
||||
for (const auto& inputMapping : device->getAvailableInputs()) {
|
||||
const auto& input = inputMapping.first;
|
||||
// Ignore aliases
|
||||
|
@ -102,6 +103,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
|
|||
}
|
||||
|
||||
_registeredDevices[deviceID] = device;
|
||||
|
||||
auto mapping = loadMappings(device->getDefaultMappingConfigs());
|
||||
if (mapping) {
|
||||
_mappingsByDevice[deviceID] = mapping;
|
||||
|
@ -111,15 +113,21 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
|
|||
emit hardwareChanged();
|
||||
}
|
||||
|
||||
// FIXME remove the associated device mappings
|
||||
void UserInputMapper::removeDevice(int deviceID) {
|
||||
|
||||
Locker locker(_lock);
|
||||
auto proxyEntry = _registeredDevices.find(deviceID);
|
||||
|
||||
if (_registeredDevices.end() == proxyEntry) {
|
||||
qCWarning(controllers) << "Attempted to remove unknown device " << deviceID;
|
||||
return;
|
||||
}
|
||||
auto proxy = proxyEntry->second;
|
||||
|
||||
auto device = proxyEntry->second;
|
||||
qCDebug(controllers) << "Unregistering input device <" << device->getName() << "> deviceID = " << deviceID;
|
||||
|
||||
unloadMappings(device->getDefaultMappingConfigs());
|
||||
|
||||
auto mappingsEntry = _mappingsByDevice.find(deviceID);
|
||||
if (_mappingsByDevice.end() != mappingsEntry) {
|
||||
disableMapping(mappingsEntry->second);
|
||||
|
@ -244,7 +252,7 @@ void UserInputMapper::update(float deltaTime) {
|
|||
for (auto& channel : _actionStates) {
|
||||
channel = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
for (auto& channel : _poseStates) {
|
||||
channel = Pose();
|
||||
}
|
||||
|
@ -705,11 +713,10 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile, bool enab
|
|||
return Mapping::Pointer();
|
||||
}
|
||||
// Each mapping only needs to be loaded once
|
||||
static QSet<QString> loaded;
|
||||
if (loaded.contains(jsonFile)) {
|
||||
if (_loadedRouteJsonFiles.contains(jsonFile)) {
|
||||
return Mapping::Pointer();
|
||||
}
|
||||
loaded.insert(jsonFile);
|
||||
_loadedRouteJsonFiles.insert(jsonFile);
|
||||
QString json;
|
||||
{
|
||||
QFile file(jsonFile);
|
||||
|
@ -741,6 +748,18 @@ MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void UserInputMapper::unloadMappings(const QStringList& jsonFiles) {
|
||||
for (const QString& jsonFile : jsonFiles) {
|
||||
unloadMapping(jsonFile);
|
||||
}
|
||||
}
|
||||
|
||||
void UserInputMapper::unloadMapping(const QString& jsonFile) {
|
||||
auto entry = _loadedRouteJsonFiles.find(jsonFile);
|
||||
if (entry != _loadedRouteJsonFiles.end()) {
|
||||
_loadedRouteJsonFiles.erase(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static const QString JSON_NAME = QStringLiteral("name");
|
||||
static const QString JSON_CHANNELS = QStringLiteral("channels");
|
||||
|
|
|
@ -111,9 +111,18 @@ namespace controller {
|
|||
|
||||
void loadDefaultMapping(uint16 deviceID);
|
||||
void enableMapping(const QString& mappingName, bool enable = true);
|
||||
|
||||
void unloadMappings(const QStringList& jsonFiles);
|
||||
void unloadMapping(const QString& jsonFile);
|
||||
|
||||
float getValue(const Input& input) const;
|
||||
Pose getPose(const Input& input) const;
|
||||
|
||||
// perform an action when the UserInputMapper mutex is acquired.
|
||||
using Locker = std::unique_lock<std::recursive_mutex>;
|
||||
template <typename F>
|
||||
void withLock(F&& f) { Locker locker(_lock); f(); }
|
||||
|
||||
signals:
|
||||
void actionEvent(int action, float state);
|
||||
void inputEvent(int input, float state);
|
||||
|
@ -177,7 +186,7 @@ namespace controller {
|
|||
RouteList _deviceRoutes;
|
||||
RouteList _standardRoutes;
|
||||
|
||||
using Locker = std::unique_lock<std::recursive_mutex>;
|
||||
QSet<QString> _loadedRouteJsonFiles;
|
||||
|
||||
mutable std::recursive_mutex _lock;
|
||||
};
|
||||
|
|
|
@ -20,8 +20,12 @@
|
|||
|
||||
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
|
||||
|
||||
void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
|
||||
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
userInputMapper->withLock([&, this]() {
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
});
|
||||
|
||||
// For touch event, we need to check that the last event is not too long ago
|
||||
// Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly
|
||||
|
|
|
@ -509,7 +509,12 @@ void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrat
|
|||
std::lock_guard<std::mutex> guard(_jointsMutex);
|
||||
joints = _joints;
|
||||
}
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints);
|
||||
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
userInputMapper->withLock([&, this]() {
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints);
|
||||
});
|
||||
|
||||
_prevJoints = joints;
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,12 @@ void SixenseManager::setSixenseFilter(bool filter) {
|
|||
|
||||
void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
|
||||
BAIL_IF_NOT_LOADED
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
userInputMapper->withLock([&, this]() {
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
});
|
||||
|
||||
if (_inputDevice->_requestReset) {
|
||||
_container->requestReset();
|
||||
_inputDevice->_requestReset = false;
|
||||
|
|
|
@ -211,9 +211,13 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
|
|||
|
||||
|
||||
void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
|
||||
// because update mutates the internal state we need to lock
|
||||
userInputMapper->withLock([&, this]() {
|
||||
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
});
|
||||
|
||||
if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) {
|
||||
userInputMapper->removeDevice(_inputDevice->_deviceID);
|
||||
_registeredWithInputMapper = false;
|
||||
|
@ -270,7 +274,8 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
|
|||
for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) {
|
||||
auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i);
|
||||
bool pressed = 0 != (controllerState.ulButtonPressed & mask);
|
||||
handleButtonEvent(deltaTime, i, pressed, isLeftHand);
|
||||
bool touched = 0 != (controllerState.ulButtonTouched & mask);
|
||||
handleButtonEvent(deltaTime, i, pressed, touched, isLeftHand);
|
||||
}
|
||||
|
||||
// process each axis
|
||||
|
@ -314,20 +319,26 @@ enum ViveButtonChannel {
|
|||
|
||||
|
||||
// These functions do translation from the Steam IDs to the standard controller IDs
|
||||
void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand) {
|
||||
if (!pressed) {
|
||||
return;
|
||||
}
|
||||
void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool touched, bool isLeftHand) {
|
||||
|
||||
using namespace controller;
|
||||
if (button == vr::k_EButton_ApplicationMenu) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LEFT_APP_MENU : RIGHT_APP_MENU);
|
||||
} else if (button == vr::k_EButton_Grip) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LB : RB);
|
||||
} else if (button == vr::k_EButton_SteamVR_Trigger) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LT : RT);
|
||||
} else if (button == vr::k_EButton_SteamVR_Touchpad) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LS : RS);
|
||||
|
||||
if (pressed) {
|
||||
if (button == vr::k_EButton_ApplicationMenu) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LEFT_APP_MENU : RIGHT_APP_MENU);
|
||||
} else if (button == vr::k_EButton_Grip) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LEFT_GRIP : RIGHT_GRIP);
|
||||
} else if (button == vr::k_EButton_SteamVR_Trigger) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LT : RT);
|
||||
} else if (button == vr::k_EButton_SteamVR_Touchpad) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LS : RS);
|
||||
}
|
||||
}
|
||||
|
||||
if (touched) {
|
||||
if (button == vr::k_EButton_SteamVR_Touchpad) {
|
||||
_buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,18 +435,28 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI
|
|||
makePair(LY, "LY"),
|
||||
makePair(RX, "RX"),
|
||||
makePair(RY, "RY"),
|
||||
// trigger analogs
|
||||
|
||||
// capacitive touch on the touch pad
|
||||
makePair(LS_TOUCH, "LSTouch"),
|
||||
makePair(RS_TOUCH, "RSTouch"),
|
||||
|
||||
// touch pad press
|
||||
makePair(LS, "LS"),
|
||||
makePair(RS, "RS"),
|
||||
|
||||
// triggers
|
||||
makePair(LT, "LT"),
|
||||
makePair(RT, "RT"),
|
||||
|
||||
makePair(LB, "LB"),
|
||||
makePair(RB, "RB"),
|
||||
// low profile side grip button.
|
||||
makePair(LEFT_GRIP, "LeftGrip"),
|
||||
makePair(RIGHT_GRIP, "RightGrip"),
|
||||
|
||||
makePair(LS, "LS"),
|
||||
makePair(RS, "RS"),
|
||||
// 3d location of controller
|
||||
makePair(LEFT_HAND, "LeftHand"),
|
||||
makePair(RIGHT_HAND, "RightHand"),
|
||||
|
||||
// app button above trackpad.
|
||||
Input::NamedPair(Input(_deviceID, LEFT_APP_MENU, ChannelType::BUTTON), "LeftApplicationMenu"),
|
||||
Input::NamedPair(Input(_deviceID, RIGHT_APP_MENU, ChannelType::BUTTON), "RightApplicationMenu"),
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ private:
|
|||
virtual void focusOutEvent() override;
|
||||
|
||||
void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
|
||||
void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand);
|
||||
void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool touched, bool isLeftHand);
|
||||
void handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand);
|
||||
void handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const mat4& mat,
|
||||
const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand);
|
||||
|
|
75
scripts/developer/tests/viveTouchpadTest.js
Normal file
75
scripts/developer/tests/viveTouchpadTest.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// viveTouchpadTest.js
|
||||
//
|
||||
// Anthony J. Thibault
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// An example of reading touch and move events from the vive controller touch pad.
|
||||
//
|
||||
// It will spawn a gray cube in front of you, then as you use the right touch pad,
|
||||
// the cube should turn green and respond to the motion of your thumb on the pad.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
var GRAY = {red: 57, green: 57, blue: 57};
|
||||
var GREEN = {red: 0, green: 255, blue: 0};
|
||||
var ZERO = {x: 0, y: 0, z: 0};
|
||||
var Y_AXIS = {x: 0, y: 1, x: 0};
|
||||
var ROT_Y_90 = Quat.angleAxis(Y_AXIS, 90.0);
|
||||
|
||||
var boxEntity;
|
||||
var boxPosition;
|
||||
var boxZAxis, boxYAxis;
|
||||
var prevThumbDown = false;
|
||||
|
||||
function init() {
|
||||
boxPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
|
||||
var front = Quat.getFront(Camera.getOrientation());
|
||||
boxZAxis = Vec3.normalize(Vec3.cross(front, Y_AXIS));
|
||||
boxYAxis = Vec3.normalize(Vec3.cross(boxZAxis, front));
|
||||
|
||||
boxEntity = Entities.addEntity({
|
||||
type: "Box",
|
||||
position: boxPosition,
|
||||
dimentions: {x: 0.25, y: 0.25, z: 0.25},
|
||||
color: GRAY,
|
||||
gravity: ZERO,
|
||||
visible: true,
|
||||
locked: false,
|
||||
lifetime: 60000
|
||||
});
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
Entities.deleteEntity(boxEntity);
|
||||
}
|
||||
Script.scriptEnding.connect(shutdown);
|
||||
|
||||
prevPose = Controller.getPoseValue(Controller.Hardware.Vive.LeftHand);
|
||||
|
||||
function update(dt) {
|
||||
var thumbDown = Controller.getValue(Controller.Hardware.Vive.RSTouch);
|
||||
if (thumbDown) {
|
||||
var x = Controller.getValue(Controller.Hardware.Vive.RX);
|
||||
var y = Controller.getValue(Controller.Hardware.Vive.RY);
|
||||
var xOffset = Vec3.multiply(boxZAxis, x);
|
||||
var yOffset = Vec3.multiply(boxYAxis, y);
|
||||
var offset = Vec3.sum(xOffset, yOffset);
|
||||
Entities.editEntity(boxEntity, {position: Vec3.sum(boxPosition, offset)});
|
||||
}
|
||||
if (thumbDown && !prevThumbDown) {
|
||||
Entities.editEntity(boxEntity, {color: GREEN});
|
||||
}
|
||||
if (!thumbDown && prevThumbDown) {
|
||||
Entities.editEntity(boxEntity, {color: GRAY});
|
||||
}
|
||||
prevThumbDown = thumbDown;
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
|
||||
init();
|
||||
|
||||
|
||||
|
Loading…
Reference in a new issue