mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-08 17:44:17 +02:00
Merge pull request #10469 from druiz17/improve-rotation-jitter
Added a LowVelocityFilter to the input system.
This commit is contained in:
commit
2b63eb9d90
13 changed files with 142 additions and 66 deletions
|
@ -34,12 +34,33 @@
|
|||
{ "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" },
|
||||
{ "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" },
|
||||
|
||||
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] },
|
||||
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] },
|
||||
{ "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] },
|
||||
{ "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", "when": [ "Application.InHMD"] },
|
||||
{ "from": "Vive.RightFoot", "to" : "Standard.RightFoot", "when": [ "Application.InHMD"] },
|
||||
{ "from": "Vive.Hips", "to" : "Standard.Hips", "when": [ "Application.InHMD"] },
|
||||
{ "from": "Vive.Spine2", "to" : "Standard.Spine2", "when": [ "Application.InHMD"] },
|
||||
|
||||
{
|
||||
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
|
||||
"when": [ "Application.InHMD"]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.RightFoot", "to" : "Standard.RightFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
|
||||
"when": [ "Application.InHMD"]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Hips", "to" : "Standard.Hips",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
|
||||
"when": [ "Application.InHMD"]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Spine2", "to" : "Standard.Spine2",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
|
||||
"when": [ "Application.InHMD"]
|
||||
},
|
||||
|
||||
{ "from": "Vive.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -3601,10 +3601,6 @@ void Application::idle(float nsecsElapsed) {
|
|||
_overlayConductor.update(secondsSinceLastUpdate);
|
||||
}
|
||||
|
||||
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
||||
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
|
||||
}
|
||||
|
||||
ivec2 Application::getMouse() const {
|
||||
return getApplicationCompositor().getReticlePosition();
|
||||
}
|
||||
|
@ -4380,6 +4376,7 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
userInputMapper->setInputCalibrationData(calibrationData);
|
||||
userInputMapper->update(deltaTime);
|
||||
|
||||
if (keyboardMousePlugin && keyboardMousePlugin->isActive()) {
|
||||
|
|
|
@ -314,7 +314,6 @@ public slots:
|
|||
void updateThreadPoolCount() const;
|
||||
void updateSystemTabletMode();
|
||||
|
||||
static void setLowVelocityFilter(bool lowVelocityFilter);
|
||||
Q_INVOKABLE void loadDialog();
|
||||
Q_INVOKABLE void loadScriptURLDialog() const;
|
||||
void toggleLogDialog();
|
||||
|
|
|
@ -540,8 +540,6 @@ Menu::Menu() {
|
|||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawHandControllers(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
|
||||
qApp, SLOT(setLowVelocityFilter(bool)));
|
||||
|
||||
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||
|
|
|
@ -1323,28 +1323,10 @@ void MyAvatar::rebuildCollisionShape() {
|
|||
_characterController.setLocalBoundingBox(corner, diagonal);
|
||||
}
|
||||
|
||||
static controller::Pose applyLowVelocityFilter(const controller::Pose& oldPose, const controller::Pose& newPose) {
|
||||
controller::Pose finalPose = newPose;
|
||||
if (newPose.isValid()) {
|
||||
// Use a velocity sensitive filter to damp small motions and preserve large ones with
|
||||
// no latency.
|
||||
float velocityFilter = glm::clamp(1.0f - glm::length(oldPose.getVelocity()), 0.0f, 1.0f);
|
||||
finalPose.translation = oldPose.getTranslation() * velocityFilter + newPose.getTranslation() * (1.0f - velocityFilter);
|
||||
finalPose.rotation = safeMix(oldPose.getRotation(), newPose.getRotation(), 1.0f - velocityFilter);
|
||||
}
|
||||
return finalPose;
|
||||
}
|
||||
|
||||
void MyAvatar::setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||
if (controller::InputDevice::getLowVelocityFilter()) {
|
||||
auto oldLeftPose = getLeftHandControllerPoseInSensorFrame();
|
||||
auto oldRightPose = getRightHandControllerPoseInSensorFrame();
|
||||
_leftHandControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
|
||||
_rightHandControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
|
||||
} else {
|
||||
_leftHandControllerPoseInSensorFrameCache.set(left);
|
||||
_rightHandControllerPoseInSensorFrameCache.set(right);
|
||||
}
|
||||
_leftHandControllerPoseInSensorFrameCache.set(left);
|
||||
_rightHandControllerPoseInSensorFrameCache.set(right);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftHandControllerPoseInSensorFrame() const {
|
||||
|
@ -1374,15 +1356,8 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
|
|||
}
|
||||
|
||||
void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||
if (controller::InputDevice::getLowVelocityFilter()) {
|
||||
auto oldLeftPose = getLeftFootControllerPoseInSensorFrame();
|
||||
auto oldRightPose = getRightFootControllerPoseInSensorFrame();
|
||||
_leftFootControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
|
||||
_rightFootControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
|
||||
} else {
|
||||
_leftFootControllerPoseInSensorFrameCache.set(left);
|
||||
_rightFootControllerPoseInSensorFrameCache.set(right);
|
||||
}
|
||||
_leftFootControllerPoseInSensorFrameCache.set(left);
|
||||
_rightFootControllerPoseInSensorFrameCache.set(right);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getLeftFootControllerPoseInSensorFrame() const {
|
||||
|
@ -1412,15 +1387,8 @@ controller::Pose MyAvatar::getRightFootControllerPoseInAvatarFrame() const {
|
|||
}
|
||||
|
||||
void MyAvatar::setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2) {
|
||||
if (controller::InputDevice::getLowVelocityFilter()) {
|
||||
auto oldHipsPose = getHipsControllerPoseInSensorFrame();
|
||||
auto oldSpine2Pose = getSpine2ControllerPoseInSensorFrame();
|
||||
_hipsControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldHipsPose, hips));
|
||||
_spine2ControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldSpine2Pose, spine2));
|
||||
} else {
|
||||
_hipsControllerPoseInSensorFrameCache.set(hips);
|
||||
_spine2ControllerPoseInSensorFrameCache.set(spine2);
|
||||
}
|
||||
_hipsControllerPoseInSensorFrameCache.set(hips);
|
||||
_spine2ControllerPoseInSensorFrameCache.set(spine2);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHipsControllerPoseInSensorFrame() const {
|
||||
|
@ -1450,12 +1418,7 @@ controller::Pose MyAvatar::getSpine2ControllerPoseInAvatarFrame() const {
|
|||
}
|
||||
|
||||
void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& head) {
|
||||
if (controller::InputDevice::getLowVelocityFilter()) {
|
||||
auto oldHeadPose = getHeadControllerPoseInSensorFrame();
|
||||
_headControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldHeadPose, head));
|
||||
} else {
|
||||
_headControllerPoseInSensorFrameCache.set(head);
|
||||
}
|
||||
_headControllerPoseInSensorFrameCache.set(head);
|
||||
}
|
||||
|
||||
controller::Pose MyAvatar::getHeadControllerPoseInSensorFrame() const {
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
|
||||
namespace controller {
|
||||
|
||||
bool InputDevice::_lowVelocityFilter = false;
|
||||
|
||||
const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
|
||||
float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
|
||||
|
||||
|
|
|
@ -77,17 +77,13 @@ public:
|
|||
static float getReticleMoveSpeed() { return _reticleMoveSpeed; }
|
||||
static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; }
|
||||
|
||||
static bool getLowVelocityFilter() { return _lowVelocityFilter; };
|
||||
|
||||
Input makeInput(StandardButtonChannel button) const;
|
||||
Input makeInput(StandardAxisChannel axis) const;
|
||||
Input makeInput(StandardPoseChannel pose) const;
|
||||
Input::NamedPair makePair(StandardButtonChannel button, const QString& name) const;
|
||||
Input::NamedPair makePair(StandardAxisChannel button, const QString& name) const;
|
||||
Input::NamedPair makePair(StandardPoseChannel button, const QString& name) const;
|
||||
public slots:
|
||||
static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; };
|
||||
|
||||
|
||||
protected:
|
||||
friend class UserInputMapper;
|
||||
|
||||
|
@ -104,8 +100,6 @@ protected:
|
|||
AxisStateMap _axisStateMap;
|
||||
PoseStateMap _poseStateMap;
|
||||
|
||||
static bool _lowVelocityFilter;
|
||||
|
||||
private:
|
||||
static float _reticleMoveSpeed;
|
||||
};
|
||||
|
|
|
@ -114,6 +114,9 @@ namespace controller {
|
|||
void loadDefaultMapping(uint16 deviceID);
|
||||
void enableMapping(const QString& mappingName, bool enable = true);
|
||||
|
||||
void setInputCalibrationData(const InputCalibrationData& data) { inputCalibrationData = data; }
|
||||
const InputCalibrationData& getInputCalibrationData() { return inputCalibrationData; }
|
||||
|
||||
void unloadMappings(const QStringList& jsonFiles);
|
||||
void unloadMapping(const QString& jsonFile);
|
||||
|
||||
|
@ -187,6 +190,8 @@ namespace controller {
|
|||
|
||||
QSet<QString> _loadedRouteJsonFiles;
|
||||
|
||||
InputCalibrationData inputCalibrationData;
|
||||
|
||||
mutable std::recursive_mutex _lock;
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "filters/TransformFilter.h"
|
||||
#include "filters/PostTransformFilter.h"
|
||||
#include "filters/RotateFilter.h"
|
||||
#include "filters/LowVelocityFilter.h"
|
||||
|
||||
using namespace controller;
|
||||
|
||||
|
@ -45,6 +46,7 @@ REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate")
|
|||
REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(LowVelocityFilter, "lowVelocity")
|
||||
|
||||
const QString JSON_FILTER_TYPE = QStringLiteral("type");
|
||||
const QString JSON_FILTER_PARAMS = QStringLiteral("params");
|
||||
|
@ -286,4 +288,4 @@ namespace controller {
|
|||
// const float _max;
|
||||
//};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "filters/TransformFilter.h"
|
||||
#include "filters/PostTransformFilter.h"
|
||||
#include "filters/RotateFilter.h"
|
||||
#include "filters/LowVelocityFilter.h"
|
||||
#include "conditionals/AndConditional.h"
|
||||
|
||||
using namespace controller;
|
||||
|
@ -127,6 +128,11 @@ QObject* RouteBuilderProxy::rotate(glm::quat rotation) {
|
|||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::lowVelocity(float rotationConstant, float translationConstant) {
|
||||
addFilter(std::make_shared<LowVelocityFilter>(rotationConstant, translationConstant));
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::constrainToInteger() {
|
||||
addFilter(std::make_shared<ConstrainToIntegerFilter>());
|
||||
return this;
|
||||
|
|
|
@ -52,6 +52,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* transform(glm::mat4 transform);
|
||||
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
|
||||
Q_INVOKABLE QObject* rotate(glm::quat rotation);
|
||||
Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant);
|
||||
|
||||
private:
|
||||
void to(const Endpoint::Pointer& destination);
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// Created by Dante Ruiz 2017/05/15
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include "LowVelocityFilter.h"
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include "../../UserInputMapper.h"
|
||||
#include "../../Input.h"
|
||||
#include <DependencyManager.h>
|
||||
|
||||
static const QString JSON_ROTATION = QStringLiteral("rotation");
|
||||
static const QString JSON_TRANSLATION = QStringLiteral("translation");
|
||||
namespace controller {
|
||||
|
||||
Pose LowVelocityFilter::apply(Pose newPose) const {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
const InputCalibrationData calibrationData = userInputMapper->getInputCalibrationData();
|
||||
glm::mat4 sensorToAvatarMat = glm::inverse(calibrationData.avatarMat) * calibrationData.sensorToWorldMat;
|
||||
glm::mat4 avatarToSensorMat = glm::inverse(calibrationData.sensorToWorldMat) * calibrationData.avatarMat;
|
||||
Pose finalPose = newPose;
|
||||
if (finalPose.isValid() && _oldPose.isValid()) {
|
||||
Pose sensorPose = finalPose.transform(avatarToSensorMat);
|
||||
Pose lastPose = _oldPose;
|
||||
|
||||
float rotationFilter = glm::clamp(1.0f - (glm::length(lastPose.getVelocity() / _rotationConstant)), 0.0f, 1.0f);
|
||||
float translationFilter = glm::clamp(1.0f - (glm::length(lastPose.getVelocity() / _translationConstant)), 0.0f, 1.0f);
|
||||
sensorPose.translation = lastPose.getTranslation() * translationFilter + sensorPose.getTranslation() * (1.0f - translationFilter);
|
||||
sensorPose.rotation = safeMix(lastPose.getRotation(), sensorPose.getRotation(), 1.0f - rotationFilter);
|
||||
|
||||
finalPose = sensorPose.transform(sensorToAvatarMat);
|
||||
}
|
||||
_oldPose = finalPose.transform(avatarToSensorMat);
|
||||
return finalPose;
|
||||
}
|
||||
|
||||
bool LowVelocityFilter::parseParameters(const QJsonValue& parameters) {
|
||||
|
||||
if (parameters.isObject()) {
|
||||
auto obj = parameters.toObject();
|
||||
if (obj.contains(JSON_ROTATION) && obj.contains(JSON_TRANSLATION)) {
|
||||
_rotationConstant = obj[JSON_ROTATION].toDouble();
|
||||
_translationConstant = obj[JSON_TRANSLATION].toDouble();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Created by Dante Ruiz 2017/05/15
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Controllers_Filters_Low_Velocity_h
|
||||
#define hifi_Controllers_Filters_Low_Velocity_h
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class LowVelocityFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(LowVelocityFilter);
|
||||
|
||||
public:
|
||||
LowVelocityFilter() {}
|
||||
LowVelocityFilter(float rotationConstant, float translationConstant) :
|
||||
_translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
|
||||
|
||||
virtual float apply(float value) const override { return value; }
|
||||
virtual Pose apply(Pose newPose) const;
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
|
||||
private:
|
||||
float _translationConstant { 0.1f };
|
||||
float _rotationConstant { 0.1f };
|
||||
mutable Pose _oldPose { Pose() };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue