mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 19:21:16 +02:00
Merge pull request #8027 from SamGondelman/exposeHaptics
Expose haptics to JS
This commit is contained in:
commit
8434aa9416
12 changed files with 234 additions and 3 deletions
|
@ -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
|
||||||
|
@ -55,6 +61,9 @@ public:
|
||||||
|
|
||||||
const QString& getName() const { return _name; }
|
const QString& getName() const { return _name; }
|
||||||
|
|
||||||
|
// By default, Input Devices do not support haptics
|
||||||
|
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
|
||||||
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData) {};
|
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData) {};
|
||||||
|
|
|
@ -140,6 +140,24 @@ namespace controller {
|
||||||
return DependencyManager::get<UserInputMapper>()->getActionNames();
|
return DependencyManager::get<UserInputMapper>()->getActionNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScriptingInterface::triggerHapticPulse(float strength, float duration, controller::Hand hand) const {
|
||||||
|
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() {
|
||||||
QVariantMap newHardware;
|
QVariantMap newHardware;
|
||||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||||
|
|
|
@ -84,6 +84,11 @@ 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(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);
|
||||||
Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); }
|
Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); }
|
||||||
|
|
|
@ -336,10 +336,28 @@ QVector<QString> UserInputMapper::getActionNames() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
||||||
|
return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, hand);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int actionMetaTypeId = qRegisterMetaType<Action>();
|
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);
|
||||||
|
@ -347,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();
|
||||||
|
@ -385,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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +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(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);
|
||||||
|
@ -199,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;
|
||||||
|
|
|
@ -21,9 +21,16 @@ Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameControl
|
||||||
InputDevice("GamePad"),
|
InputDevice("GamePad"),
|
||||||
_sdlGameController(sdlGameController),
|
_sdlGameController(sdlGameController),
|
||||||
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
|
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
|
||||||
|
_sdlHaptic(SDL_HapticOpenFromJoystick(_sdlJoystick)),
|
||||||
_instanceId(instanceId)
|
_instanceId(instanceId)
|
||||||
{
|
{
|
||||||
|
if (!_sdlHaptic) {
|
||||||
|
qDebug() << "SDL Haptic Open Failure: " << QString(SDL_GetError());
|
||||||
|
} else {
|
||||||
|
if (SDL_HapticRumbleInit(_sdlHaptic) != 0) {
|
||||||
|
qDebug() << "SDL Haptic Rumble Init Failure: " << QString(SDL_GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Joystick::~Joystick() {
|
Joystick::~Joystick() {
|
||||||
|
@ -31,6 +38,9 @@ Joystick::~Joystick() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Joystick::closeJoystick() {
|
void Joystick::closeJoystick() {
|
||||||
|
if (_sdlHaptic) {
|
||||||
|
SDL_HapticClose(_sdlHaptic);
|
||||||
|
}
|
||||||
SDL_GameControllerClose(_sdlGameController);
|
SDL_GameControllerClose(_sdlGameController);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +72,13 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Joystick::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
|
||||||
|
if (SDL_HapticRumblePlay(_sdlHaptic, strength, duration) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
controller::Input::NamedVector Joystick::getAvailableInputs() const {
|
controller::Input::NamedVector Joystick::getAvailableInputs() const {
|
||||||
using namespace controller;
|
using namespace controller;
|
||||||
static const Input::NamedVector availableInputs{
|
static const Input::NamedVector availableInputs{
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
virtual QString getDefaultMappingConfig() const override;
|
virtual QString getDefaultMappingConfig() const override;
|
||||||
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, controller::Hand hand) override;
|
||||||
|
|
||||||
Joystick() : InputDevice("GamePad") {}
|
Joystick() : InputDevice("GamePad") {}
|
||||||
~Joystick();
|
~Joystick();
|
||||||
|
@ -52,6 +54,7 @@ public:
|
||||||
private:
|
private:
|
||||||
SDL_GameController* _sdlGameController;
|
SDL_GameController* _sdlGameController;
|
||||||
SDL_Joystick* _sdlJoystick;
|
SDL_Joystick* _sdlJoystick;
|
||||||
|
SDL_Haptic* _sdlHaptic;
|
||||||
SDL_JoystickID _instanceId;
|
SDL_JoystickID _instanceId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL2Manager::init() {
|
void SDL2Manager::init() {
|
||||||
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0);
|
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0);
|
||||||
|
|
||||||
if (initSuccess) {
|
if (initSuccess) {
|
||||||
int joystickCount = SDL_NumJoysticks();
|
int joystickCount = SDL_NumJoysticks();
|
||||||
|
|
|
@ -105,6 +105,12 @@ void OculusControllerManager::pluginFocusOutEvent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OculusControllerManager::stopHapticPulse(bool leftHand) {
|
||||||
|
if (_touch) {
|
||||||
|
_touch->stopHapticPulse(leftHand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using namespace controller;
|
using namespace controller;
|
||||||
|
|
||||||
static const std::vector<std::pair<ovrButton, StandardButtonChannel>> BUTTON_MAP { {
|
static const std::vector<std::pair<ovrButton, StandardButtonChannel>> BUTTON_MAP { {
|
||||||
|
@ -212,6 +218,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 * 1000.0f; // milliseconds
|
||||||
|
} else {
|
||||||
|
stopHapticPulse(true);
|
||||||
|
}
|
||||||
|
if (_rightHapticDuration > 0.0f) {
|
||||||
|
_rightHapticDuration -= deltaTime * 1000.0f; // milliseconds
|
||||||
|
} else {
|
||||||
|
stopHapticPulse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OculusControllerManager::TouchDevice::focusOutEvent() {
|
void OculusControllerManager::TouchDevice::focusOutEvent() {
|
||||||
|
@ -309,6 +330,41 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
|
||||||
pose = pose.transform(controllerToAvatar);
|
pose = pose.transform(controllerToAvatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
|
||||||
|
Locker locker(_lock);
|
||||||
|
bool toReturn = true;
|
||||||
|
if (hand == controller::BOTH || hand == controller::LEFT) {
|
||||||
|
if (strength == 0.0f) {
|
||||||
|
_leftHapticStrength = 0.0f;
|
||||||
|
_leftHapticDuration = 0.0f;
|
||||||
|
} else {
|
||||||
|
_leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength;
|
||||||
|
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) {
|
||||||
|
toReturn = false;
|
||||||
|
}
|
||||||
|
_leftHapticDuration = std::max(duration, _leftHapticDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hand == controller::BOTH || hand == controller::RIGHT) {
|
||||||
|
if (strength == 0.0f) {
|
||||||
|
_rightHapticStrength = 0.0f;
|
||||||
|
_rightHapticDuration = 0.0f;
|
||||||
|
} else {
|
||||||
|
_rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength;
|
||||||
|
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) {
|
||||||
|
toReturn = false;
|
||||||
|
}
|
||||||
|
_rightHapticDuration = std::max(duration, _rightHapticDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
|
||||||
|
auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch);
|
||||||
|
ovr_SetControllerVibration(_parent._session, handType, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const {
|
controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const {
|
||||||
using namespace controller;
|
using namespace controller;
|
||||||
QVector<Input::NamedPair> availableInputs{
|
QVector<Input::NamedPair> availableInputs{
|
||||||
|
|
|
@ -32,6 +32,9 @@ public:
|
||||||
void pluginFocusOutEvent() override;
|
void pluginFocusOutEvent() override;
|
||||||
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void stopHapticPulse(bool leftHand);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class OculusInputDevice : public controller::InputDevice {
|
class OculusInputDevice : public controller::InputDevice {
|
||||||
public:
|
public:
|
||||||
|
@ -64,9 +67,24 @@ 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, controller::Hand hand) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
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 _leftHapticStrength { 0.0f };
|
||||||
|
float _rightHapticDuration { 0.0f };
|
||||||
|
float _rightHapticStrength { 0.0f };
|
||||||
|
mutable std::recursive_mutex _lock;
|
||||||
|
|
||||||
friend class OculusControllerManager;
|
friend class OculusControllerManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,6 @@ bool ViveControllerManager::activate() {
|
||||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||||
userInputMapper->registerDevice(_inputDevice);
|
userInputMapper->registerDevice(_inputDevice);
|
||||||
_registeredWithInputMapper = true;
|
_registeredWithInputMapper = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +249,17 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle
|
||||||
handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true);
|
handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true);
|
||||||
handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false);
|
handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false);
|
||||||
|
|
||||||
|
// handle haptics
|
||||||
|
{
|
||||||
|
Locker locker(_lock);
|
||||||
|
if (_leftHapticDuration > 0.0f) {
|
||||||
|
hapticsHelper(deltaTime, true);
|
||||||
|
}
|
||||||
|
if (_rightHapticDuration > 0.0f) {
|
||||||
|
hapticsHelper(deltaTime, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int numTrackedControllers = 0;
|
int numTrackedControllers = 0;
|
||||||
if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) {
|
if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) {
|
||||||
numTrackedControllers++;
|
numTrackedControllers++;
|
||||||
|
@ -447,6 +457,55 @@ 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, controller::Hand hand) {
|
||||||
|
Locker locker(_lock);
|
||||||
|
if (hand == controller::BOTH || hand == controller::LEFT) {
|
||||||
|
if (strength == 0.0f) {
|
||||||
|
_leftHapticStrength = 0.0f;
|
||||||
|
_leftHapticDuration = 0.0f;
|
||||||
|
} else {
|
||||||
|
_leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength;
|
||||||
|
_leftHapticDuration = std::max(duration, _leftHapticDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hand == controller::BOTH || hand == controller::RIGHT) {
|
||||||
|
if (strength == 0.0f) {
|
||||||
|
_rightHapticStrength = 0.0f;
|
||||||
|
_rightHapticDuration = 0.0f;
|
||||||
|
} else {
|
||||||
|
_rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength;
|
||||||
|
_rightHapticDuration = std::max(duration, _rightHapticDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool leftHand) {
|
||||||
|
auto handRole = leftHand ? vr::TrackedControllerRole_LeftHand : vr::TrackedControllerRole_RightHand;
|
||||||
|
auto deviceIndex = _system->GetTrackedDeviceIndexForControllerRole(handRole);
|
||||||
|
|
||||||
|
if (_system->IsTrackedDeviceConnected(deviceIndex) &&
|
||||||
|
_system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller &&
|
||||||
|
_trackedDevicePose[deviceIndex].bPoseIsValid) {
|
||||||
|
float strength = leftHand ? _leftHapticStrength : _rightHapticStrength;
|
||||||
|
float duration = leftHand ? _leftHapticDuration : _rightHapticDuration;
|
||||||
|
|
||||||
|
// Vive Controllers only support duration up to 4 ms, which is short enough that any variation feels more like strength
|
||||||
|
const float MAX_HAPTIC_TIME = 3999.0f; // in microseconds
|
||||||
|
float hapticTime = strength * MAX_HAPTIC_TIME;
|
||||||
|
if (hapticTime < duration * 1000.0f) {
|
||||||
|
_system->TriggerHapticPulse(deviceIndex, 0, hapticTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
float remainingHapticTime = duration - (hapticTime / 1000.0f + deltaTime * 1000.0f); // in milliseconds
|
||||||
|
if (leftHand) {
|
||||||
|
_leftHapticDuration = remainingHapticTime;
|
||||||
|
} else {
|
||||||
|
_rightHapticDuration = remainingHapticTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const {
|
controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const {
|
||||||
using namespace controller;
|
using namespace controller;
|
||||||
QVector<Input::NamedPair> availableInputs{
|
QVector<Input::NamedPair> availableInputs{
|
||||||
|
|
|
@ -56,6 +56,9 @@ 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, controller::Hand hand) override;
|
||||||
|
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);
|
||||||
void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool touched, 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 handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand);
|
||||||
|
@ -90,8 +93,19 @@ private:
|
||||||
FilteredStick _filteredLeftStick;
|
FilteredStick _filteredLeftStick;
|
||||||
FilteredStick _filteredRightStick;
|
FilteredStick _filteredRightStick;
|
||||||
|
|
||||||
|
// perform an action when the InputDevice mutex is acquired.
|
||||||
|
using Locker = std::unique_lock<std::recursive_mutex>;
|
||||||
|
template <typename F>
|
||||||
|
void withLock(F&& f) { Locker locker(_lock); f(); }
|
||||||
|
|
||||||
int _trackedControllers { 0 };
|
int _trackedControllers { 0 };
|
||||||
vr::IVRSystem*& _system;
|
vr::IVRSystem*& _system;
|
||||||
|
float _leftHapticStrength { 0.0f };
|
||||||
|
float _leftHapticDuration { 0.0f };
|
||||||
|
float _rightHapticStrength { 0.0f };
|
||||||
|
float _rightHapticDuration { 0.0f };
|
||||||
|
mutable std::recursive_mutex _lock;
|
||||||
|
|
||||||
friend class ViveControllerManager;
|
friend class ViveControllerManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue