added options for both hands (default), all devices, short pulse, and fixed touch timing mechanism

This commit is contained in:
SamGondelman 2016-06-06 15:03:08 -07:00
parent afca0ec2c9
commit e7743cd8e2
11 changed files with 105 additions and 41 deletions

View file

@ -31,6 +31,12 @@ namespace controller {
class Endpoint; class Endpoint;
using EndpointPointer = std::shared_ptr<Endpoint>; using EndpointPointer = std::shared_ptr<Endpoint>;
enum Hand {
LEFT = 0,
RIGHT,
BOTH
};
// NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first. // NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.
// e.g. class Example : public InputPlugin, public InputDevice // e.g. class Example : public InputPlugin, public InputDevice
// instead of class Example : public InputDevice, public InputPlugin // instead of class Example : public InputDevice, public InputPlugin
@ -56,7 +62,7 @@ public:
const QString& getName() const { return _name; } const QString& getName() const { return _name; }
// By default, Input Devices do not support haptics // By default, Input Devices do not support haptics
virtual bool triggerHapticPulse(float strength, float duration, bool leftHand) { return false; } virtual bool triggerHapticPulse(float strength, float duration, controller::Hand hand) { return false; }
// Update call MUST be called once per simulation loop // Update call MUST be called once per simulation loop
// It takes care of updating the action states and deltas // It takes care of updating the action states and deltas

View file

@ -140,8 +140,22 @@ namespace controller {
return DependencyManager::get<UserInputMapper>()->getActionNames(); return DependencyManager::get<UserInputMapper>()->getActionNames();
} }
bool ScriptingInterface::triggerHapticPulse(unsigned int device, float strength, float duration, bool leftHand) const { bool ScriptingInterface::triggerHapticPulse(float strength, float duration, controller::Hand hand) const {
return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(device, strength, duration, leftHand); return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(strength, duration, hand);
}
bool ScriptingInterface::triggerShortHapticPulse(float strength, controller::Hand hand) const {
const float SHORT_HAPTIC_DURATION_MS = 250.0f;
return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, hand);
}
bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand) const {
return DependencyManager::get<UserInputMapper>()->triggerHapticPulseOnDevice(device, strength, duration, hand);
}
bool ScriptingInterface::triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand) const {
const float SHORT_HAPTIC_DURATION_MS = 250.0f;
return DependencyManager::get<UserInputMapper>()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, hand);
} }
void ScriptingInterface::updateMaps() { void ScriptingInterface::updateMaps() {

View file

@ -84,7 +84,10 @@ namespace controller {
Q_INVOKABLE Pose getPoseValue(const int& source) const; Q_INVOKABLE Pose getPoseValue(const int& source) const;
Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const; Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
Q_INVOKABLE bool triggerHapticPulse(unsigned int device, float strength, float duration, bool leftHand = true) const; Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, controller::Hand hand = BOTH) const;
Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const;
Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand = BOTH) const;
Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand = BOTH) const;
Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);

View file

@ -336,10 +336,19 @@ QVector<QString> UserInputMapper::getActionNames() const {
return result; return result;
} }
bool UserInputMapper::triggerHapticPulse(uint16 deviceID, float strength, float duration, bool leftHand) { bool UserInputMapper::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock);
bool toReturn = false;
for (auto device : _registeredDevices) {
toReturn = toReturn || device.second->triggerHapticPulse(strength, duration, hand);
}
return toReturn;
}
bool UserInputMapper::triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand) {
Locker locker(_lock); Locker locker(_lock);
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, leftHand); return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, hand);
} }
return false; return false;
} }
@ -348,6 +357,7 @@ int actionMetaTypeId = qRegisterMetaType<Action>();
int inputMetaTypeId = qRegisterMetaType<Input>(); int inputMetaTypeId = qRegisterMetaType<Input>();
int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>(); int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>();
int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose"); int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose");
int handMetaTypeId = qRegisterMetaType<controller::Hand>();
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input); QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
void inputFromScriptValue(const QScriptValue& object, Input& input); void inputFromScriptValue(const QScriptValue& object, Input& input);
@ -355,6 +365,8 @@ QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action);
void actionFromScriptValue(const QScriptValue& object, Action& action); void actionFromScriptValue(const QScriptValue& object, Action& action);
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair); QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair);
void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair); void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair);
QScriptValue handToScriptValue(QScriptEngine* engine, const controller::Hand& hand);
void handFromScriptValue(const QScriptValue& object, controller::Hand& hand);
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) { QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) {
QScriptValue obj = engine->newObject(); QScriptValue obj = engine->newObject();
@ -393,12 +405,21 @@ void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inpu
inputPair.second = QString(object.property("inputName").toVariant().toString()); inputPair.second = QString(object.property("inputName").toVariant().toString());
} }
QScriptValue handToScriptValue(QScriptEngine* engine, const controller::Hand& hand) {
return engine->newVariant((int)hand);
}
void handFromScriptValue(const QScriptValue& object, controller::Hand& hand) {
hand = Hand(object.toVariant().toInt());
}
void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { void UserInputMapper::registerControllerTypes(QScriptEngine* engine) {
qScriptRegisterSequenceMetaType<QVector<Action> >(engine); qScriptRegisterSequenceMetaType<QVector<Action> >(engine);
qScriptRegisterSequenceMetaType<Input::NamedVector>(engine); qScriptRegisterSequenceMetaType<Input::NamedVector>(engine);
qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
qScriptRegisterMetaType(engine, handToScriptValue, handFromScriptValue);
qScriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue); qScriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue);
} }

View file

@ -89,7 +89,8 @@ namespace controller {
void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; } void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; }
void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; } void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; }
void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; } void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
bool triggerHapticPulse(uint16 deviceID, float strength, float duration, bool leftHand); bool triggerHapticPulse(float strength, float duration, controller::Hand hand);
bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand);
static Input makeStandardInput(controller::StandardButtonChannel button); static Input makeStandardInput(controller::StandardButtonChannel button);
static Input makeStandardInput(controller::StandardAxisChannel axis); static Input makeStandardInput(controller::StandardAxisChannel axis);
@ -200,6 +201,7 @@ Q_DECLARE_METATYPE(QVector<controller::Input::NamedPair>)
Q_DECLARE_METATYPE(controller::Input) Q_DECLARE_METATYPE(controller::Input)
Q_DECLARE_METATYPE(controller::Action) Q_DECLARE_METATYPE(controller::Action)
Q_DECLARE_METATYPE(QVector<controller::Action>) Q_DECLARE_METATYPE(QVector<controller::Action>)
Q_DECLARE_METATYPE(controller::Hand)
// Cheating. // Cheating.
using UserInputMapper = controller::UserInputMapper; using UserInputMapper = controller::UserInputMapper;

View file

@ -67,7 +67,7 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
} }
} }
bool Joystick::triggerHapticPulse(float strength, float duration, bool leftHand) { bool Joystick::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
if (SDL_HapticRumblePlay(_sdlHaptic, strength, duration) != 0) { if (SDL_HapticRumblePlay(_sdlHaptic, strength, duration) != 0) {
return false; return false;
} }

View file

@ -37,7 +37,7 @@ public:
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
virtual void focusOutEvent() override; virtual void focusOutEvent() override;
bool triggerHapticPulse(float strength, float duration, bool leftHand) override; bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override;
Joystick() : InputDevice("GamePad") {} Joystick() : InputDevice("GamePad") {}
~Joystick(); ~Joystick();

View file

@ -55,11 +55,6 @@ bool OculusControllerManager::activate() {
userInputMapper->registerDevice(_touch); userInputMapper->registerDevice(_touch);
} }
_leftHapticTimer.setSingleShot(true);
_rightHapticTimer.setSingleShot(true);
connect(&_leftHapticTimer, SIGNAL(timeout()), this, SLOT(stopHapticPulse(true)));
connect(&_rightHapticTimer, SIGNAL(timeout()), this, SLOT(stopHapticPulse(false)));
return true; return true;
} }
@ -221,6 +216,21 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control
_buttonPressedMap.insert(pair.second); _buttonPressedMap.insert(pair.second);
} }
} }
// Haptics
{
Locker locker(_lock);
if (_leftHapticDuration > 0.0f) {
_leftHapticDuration -= deltaTime;
} else {
stopHapticPulse(true);
}
if (_rightHapticDuration > 0.0f) {
_rightHapticDuration -= deltaTime;
} else {
stopHapticPulse(false);
}
}
} }
void OculusControllerManager::TouchDevice::focusOutEvent() { void OculusControllerManager::TouchDevice::focusOutEvent() {
@ -239,19 +249,22 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
pose.velocity = toGlm(handPose.LinearVelocity); pose.velocity = toGlm(handPose.LinearVelocity);
} }
bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, bool leftHand) { bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch); Locker locker(_lock);
if (ovr_SetControllerVibration(_parent._session, handType, 1.0f, strength) != ovrSuccess) { bool toReturn = true;
return false; if (hand == controller::BOTH || hand == controller::LEFT) {
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_LTouch, 1.0f, strength) != ovrSuccess) {
toReturn = false;
} }
_leftHapticDuration = duration;
if (leftHand) {
_parent._leftHapticTimer.start(duration);
} else {
_parent._rightHapticTimer.start(duration);
} }
if (hand == controller::BOTH || hand == controller::RIGHT) {
return true; if (ovr_SetControllerVibration(_parent._session, ovrControllerType_RTouch, 1.0f, strength) != ovrSuccess) {
toReturn = false;
}
_rightHapticDuration = duration;
}
return toReturn;
} }
void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) { void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {

View file

@ -10,7 +10,6 @@
#define hifi__OculusControllerManager #define hifi__OculusControllerManager
#include <QObject> #include <QObject>
#include <QTimer>
#include <unordered_set> #include <unordered_set>
#include <GLMHelpers.h> #include <GLMHelpers.h>
@ -68,20 +67,28 @@ private:
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
void focusOutEvent() override; void focusOutEvent() override;
bool triggerHapticPulse(float strength, float duration, bool leftHand) override; bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override;
private: private:
void stopHapticPulse(bool leftHand); void stopHapticPulse(bool leftHand);
void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose); void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose);
int _trackedControllers { 0 }; int _trackedControllers { 0 };
// perform an action when the TouchDevice mutex is acquired.
using Locker = std::unique_lock<std::recursive_mutex>;
template <typename F>
void withLock(F&& f) { Locker locker(_lock); f(); }
float _leftHapticDuration { 0.0f };
float _rightHapticDuration { 0.0f };
mutable std::recursive_mutex _lock;
friend class OculusControllerManager; friend class OculusControllerManager;
}; };
ovrSession _session { nullptr }; ovrSession _session { nullptr };
ovrInputState _inputState {}; ovrInputState _inputState {};
RemoteDevice::Pointer _remote; RemoteDevice::Pointer _remote;
QTimer _leftHapticTimer;
QTimer _rightHapticTimer;
TouchDevice::Pointer _touch; TouchDevice::Pointer _touch;
static const QString NAME; static const QString NAME;
}; };

View file

@ -452,12 +452,13 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const
_poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar); _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar);
} }
bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, bool leftHand) { bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock); Locker locker(_lock);
if (leftHand) { if (hand == controller::BOTH || hand == controller::LEFT) {
_leftHapticStrength = strength; _leftHapticStrength = strength;
_leftHapticDuration = duration; _leftHapticDuration = duration;
} else { }
if (hand == controller::BOTH || hand == controller::RIGHT) {
_rightHapticStrength = strength; _rightHapticStrength = strength;
_rightHapticDuration = duration; _rightHapticDuration = duration;
} }
@ -481,9 +482,6 @@ void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool lef
_system->TriggerHapticPulse(deviceIndex, 0, hapticTime); _system->TriggerHapticPulse(deviceIndex, 0, hapticTime);
} }
// Must wait 5 ms before triggering another pulse on this controller
// https://github.com/ValveSoftware/openvr/wiki/IVRSystem::TriggerHapticPulse
const float HAPTIC_RESET_TIME = 5.0f;
float remainingHapticTime = duration - (hapticTime / 1000.0f + deltaTime * 1000.0f); // in milliseconds float remainingHapticTime = duration - (hapticTime / 1000.0f + deltaTime * 1000.0f); // in milliseconds
if (leftHand) { if (leftHand) {
_leftHapticDuration = remainingHapticTime; _leftHapticDuration = remainingHapticTime;

View file

@ -48,7 +48,7 @@ public:
private: private:
class InputDevice : public controller::InputDevice { class InputDevice : public controller::InputDevice {
public: public:
InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system), _leftHapticDuration(0.0f), _rightHapticDuration(0.0f) {} InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) {}
private: private:
// Device functions // Device functions
controller::Input::NamedVector getAvailableInputs() const override; controller::Input::NamedVector getAvailableInputs() const override;
@ -56,7 +56,7 @@ private:
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
void focusOutEvent() override; void focusOutEvent() override;
bool triggerHapticPulse(float strength, float duration, bool leftHand) override; bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override;
void hapticsHelper(float deltaTime, bool leftHand); void hapticsHelper(float deltaTime, bool leftHand);
void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
@ -100,10 +100,10 @@ private:
int _trackedControllers { 0 }; int _trackedControllers { 0 };
vr::IVRSystem*& _system; vr::IVRSystem*& _system;
float _leftHapticStrength; float _leftHapticStrength { 0.0f };
float _leftHapticDuration; float _leftHapticDuration { 0.0f };
float _rightHapticStrength; float _rightHapticStrength { 0.0f };
float _rightHapticDuration; float _rightHapticDuration { 0.0f };
mutable std::recursive_mutex _lock; mutable std::recursive_mutex _lock;
friend class ViveControllerManager; friend class ViveControllerManager;