Added OutOfRangeDataStrategy parameter to Controller Settings

The openvr SDK provides a way to gauge the quality of tracking on a given device via the eTrackingResult enum.

* Drop - Only Running_OK is considered valid, all other eTrackingResults will return invalid poses.
* Freeze - Only Running_OK is considered valid, but other valid TrackingResults will return the last Running_OK pose.
  In esseces this will freeze the puck in place at the last good value.
* None - All valid eTrackingResults will be valid, including OutOfRange and Calibrating results.  This is the default.
This commit is contained in:
Anthony Thibault 2018-09-19 16:08:16 -07:00
parent d15cc86735
commit 7777f3edd0
4 changed files with 118 additions and 39 deletions

View file

@ -51,17 +51,8 @@
{ "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" }, { "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" },
{ "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" }, { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" },
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand", { "from": "Vive.LeftHand", "to": "Standard.LeftHand"},
"filters" : [{"type" : "accelerationLimiter", { "from": "Vive.RightHand", "to": "Standard.RightHand"},
"rotationAccelerationLimit" : 4000.0, "rotationDecelerationLimit" : 8000.0,
"translationAccelerationLimit": 200.0, "translationDecelerationLimit": 400.0}]
},
{ "from": "Vive.RightHand", "to": "Standard.RightHand",
"filters" : [{"type" : "accelerationLimiter",
"rotationAccelerationLimit" : 2000.0, "rotationDecelerationLimit" : 4000.0,
"translationAccelerationLimit": 100.0, "translationDecelerationLimit": 200.0}]
},
{ {
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",

View file

@ -822,11 +822,44 @@ Flickable {
} }
} }
Row {
id: outOfRangeDataStrategyRow
anchors.top: viveInDesktop.bottom
anchors.topMargin: 5
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 15
RalewayRegular {
id: outOfRangeDataStrategyLabel
size: 12
text: "Out Of Range Data Strategy:"
color: hifi.colors.lightGrayText
topPadding: 5
}
HifiControls.ComboBox {
id: outOfRangeDataStrategyComboBox
height: 25
width: 100
editable: true
colorScheme: hifi.colorSchemes.dark
model: ["None", "Freeze", "Drop"]
label: ""
onCurrentIndexChanged: {
sendConfigurationSettings();
}
}
}
RalewayBold { RalewayBold {
id: viveDesktopText id: viveDesktopText
size: 10 size: 12
text: "Use " + stack.selectedPlugin + " devices in desktop mode" text: "Use " + stack.selectedPlugin + " devices in desktop mode"
color: hifi.colors.white color: hifi.colors.lightGrayText
anchors { anchors {
left: viveInDesktop.right left: viveInDesktop.right
@ -946,6 +979,7 @@ Flickable {
viveInDesktop.checked = desktopMode; viveInDesktop.checked = desktopMode;
hmdInDesktop.checked = hmdDesktopPosition; hmdInDesktop.checked = hmdDesktopPosition;
outOfRangeDataStrategyComboBox.currentIndex = outOfRangeDataStrategyComboBox.model.indexOf(settings.outOfRangeDataStrategy);
initializeButtonState(); initializeButtonState();
updateCalibrationText(); updateCalibrationText();
@ -1107,7 +1141,8 @@ Flickable {
"armCircumference": armCircumference.realValue, "armCircumference": armCircumference.realValue,
"shoulderWidth": shoulderWidth.realValue, "shoulderWidth": shoulderWidth.realValue,
"desktopMode": viveInDesktop.checked, "desktopMode": viveInDesktop.checked,
"hmdDesktopTracking": hmdInDesktop.checked "hmdDesktopTracking": hmdInDesktop.checked,
"outOfRangeDataStrategy": outOfRangeDataStrategyComboBox.model[outOfRangeDataStrategyComboBox.currentIndex]
} }
return settingsObject; return settingsObject;

View file

@ -129,6 +129,28 @@ static glm::mat4 calculateResetMat() {
return glm::mat4(); return glm::mat4();
} }
static QString outOfRangeDataStrategyToString(ViveControllerManager::OutOfRangeDataStrategy strategy) {
switch (strategy) {
default:
case ViveControllerManager::OutOfRangeDataStrategy::None:
return "None";
case ViveControllerManager::OutOfRangeDataStrategy::Freeze:
return "Freeze";
case ViveControllerManager::OutOfRangeDataStrategy::Drop:
return "Drop";
}
}
static ViveControllerManager::OutOfRangeDataStrategy stringToOutOfRangeDataStrategy(const QString& string) {
if (string == "Drop") {
return ViveControllerManager::OutOfRangeDataStrategy::Drop;
} else if (string == "Freeze") {
return ViveControllerManager::OutOfRangeDataStrategy::Freeze;
} else {
return ViveControllerManager::OutOfRangeDataStrategy::None;
}
}
bool ViveControllerManager::isDesktopMode() { bool ViveControllerManager::isDesktopMode() {
if (_container) { if (_container) {
return !_container->getActiveDisplayPlugin()->isHmd(); return !_container->getActiveDisplayPlugin()->isHmd();
@ -288,8 +310,10 @@ void ViveControllerManager::loadSettings() {
if (_inputDevice) { if (_inputDevice) {
const double DEFAULT_ARM_CIRCUMFERENCE = 0.33; const double DEFAULT_ARM_CIRCUMFERENCE = 0.33;
const double DEFAULT_SHOULDER_WIDTH = 0.48; const double DEFAULT_SHOULDER_WIDTH = 0.48;
const QString DEFAULT_OUT_OF_RANGE_STRATEGY = "None";
_inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble(); _inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble();
_inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble(); _inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble();
_inputDevice->_outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(settings.value("outOfRangeDataStrategy", QVariant(DEFAULT_OUT_OF_RANGE_STRATEGY)).toString());
} }
} }
settings.endGroup(); settings.endGroup();
@ -303,6 +327,7 @@ void ViveControllerManager::saveSettings() const {
if (_inputDevice) { if (_inputDevice) {
settings.setValue(QString("armCircumference"), _inputDevice->_armCircumference); settings.setValue(QString("armCircumference"), _inputDevice->_armCircumference);
settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth); settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth);
settings.setValue(QString("outOfRangeDataStrategy"), outOfRangeDataStrategyToString(_inputDevice->_outOfRangeDataStrategy));
} }
} }
settings.endGroup(); settings.endGroup();
@ -446,6 +471,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso
hmdDesktopTracking = iter.value().toBool(); hmdDesktopTracking = iter.value().toBool();
} else if (iter.key() == "desktopMode") { } else if (iter.key() == "desktopMode") {
hmdDesktopMode = iter.value().toBool(); hmdDesktopMode = iter.value().toBool();
} else if (iter.key() == "outOfRangeDataStrategy") {
_outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(iter.value().toString());
} }
iter++; iter++;
} }
@ -468,6 +495,7 @@ QJsonObject ViveControllerManager::InputDevice::configurationSettings() {
configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); configurationSettings["puckCount"] = (int)_validTrackedObjects.size();
configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM; configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM;
configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM; configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM;
configurationSettings["outOfRangeDataStrategy"] = outOfRangeDataStrategyToString(_outOfRangeDataStrategy);
return configurationSettings; return configurationSettings;
} }
@ -484,6 +512,10 @@ void ViveControllerManager::InputDevice::emitCalibrationStatus() {
emit inputConfiguration->calibrationStatus(status); emit inputConfiguration->calibrationStatus(status);
} }
static controller::Pose buildPose(const glm::mat4& mat, const glm::vec3& linearVelocity, const glm::vec3& angularVelocity) {
return controller::Pose(extractTranslation(mat), glmExtractRotation(mat), linearVelocity, angularVelocity);
}
void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) { void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) {
uint32_t poseIndex = controller::TRACKED_OBJECT_00 + deviceIndex; uint32_t poseIndex = controller::TRACKED_OBJECT_00 + deviceIndex;
printDeviceTrackingResultChange(deviceIndex); printDeviceTrackingResultChange(deviceIndex);
@ -492,35 +524,48 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde
_nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid && _nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid &&
poseIndex <= controller::TRACKED_OBJECT_15) { poseIndex <= controller::TRACKED_OBJECT_15) {
mat4& mat = mat4(); controller::Pose pose;
vec3 linearVelocity = vec3(); switch (_outOfRangeDataStrategy) {
vec3 angularVelocity = vec3(); case OutOfRangeDataStrategy::Drop:
// check if the device is tracking out of range, then process the correct pose depending on the result. default:
if (_nextSimPoseData.vrPoses[deviceIndex].eTrackingResult != vr::TrackingResult_Running_OutOfRange) { // Drop - Mark all non Running_OK results as invald
mat = _nextSimPoseData.poses[deviceIndex]; if (_nextSimPoseData.vrPoses[deviceIndex].eTrackingResult == vr::TrackingResult_Running_OK) {
linearVelocity = _nextSimPoseData.linearVelocities[deviceIndex]; pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]);
angularVelocity = _nextSimPoseData.angularVelocities[deviceIndex]; } else {
} else { pose.valid = false;
mat = _lastSimPoseData.poses[deviceIndex]; }
linearVelocity = _lastSimPoseData.linearVelocities[deviceIndex]; break;
angularVelocity = _lastSimPoseData.angularVelocities[deviceIndex]; case OutOfRangeDataStrategy::None:
// None - Ignore eTrackingResult all together
// make sure that we do not overwrite the pose in the _lastSimPose with incorrect data. pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]);
_nextSimPoseData.poses[deviceIndex] = _lastSimPoseData.poses[deviceIndex]; break;
_nextSimPoseData.linearVelocities[deviceIndex] = _lastSimPoseData.linearVelocities[deviceIndex]; case OutOfRangeDataStrategy::Freeze:
_nextSimPoseData.angularVelocities[deviceIndex] = _lastSimPoseData.angularVelocities[deviceIndex]; // Freeze - Dont invalide non Running_OK poses, instead just return the last good pose.
if (_nextSimPoseData.vrPoses[deviceIndex].eTrackingResult == vr::TrackingResult_Running_OK) {
pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]);
} else {
pose = buildPose(_lastSimPoseData.poses[deviceIndex], _lastSimPoseData.linearVelocities[deviceIndex], _lastSimPoseData.angularVelocities[deviceIndex]);
// make sure that we do not overwrite the pose in the _lastSimPose with incorrect data.
_nextSimPoseData.poses[deviceIndex] = _lastSimPoseData.poses[deviceIndex];
_nextSimPoseData.linearVelocities[deviceIndex] = _lastSimPoseData.linearVelocities[deviceIndex];
_nextSimPoseData.angularVelocities[deviceIndex] = _lastSimPoseData.angularVelocities[deviceIndex];
}
break;
} }
controller::Pose pose(extractTranslation(mat), glmExtractRotation(mat), linearVelocity, angularVelocity); if (pose.valid) {
// transform into avatar frame
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
_poseStateMap[poseIndex] = pose.transform(controllerToAvatar);
// transform into avatar frame // but _validTrackedObjects remain in sensor frame
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; _validTrackedObjects.push_back(std::make_pair(poseIndex, pose));
_poseStateMap[poseIndex] = pose.transform(controllerToAvatar); _trackedControllers++;
} else {
// but _validTrackedObjects remain in sensor frame // insert invalid pose into state map
_validTrackedObjects.push_back(std::make_pair(poseIndex, pose)); _poseStateMap[poseIndex] = pose;
_trackedControllers++; }
} else { } else {
controller::Pose invalidPose; controller::Pose invalidPose;
_poseStateMap[poseIndex] = invalidPose; _poseStateMap[poseIndex] = invalidPose;

View file

@ -60,11 +60,18 @@ public:
virtual void saveSettings() const override; virtual void saveSettings() const override;
virtual void loadSettings() override; virtual void loadSettings() override;
enum class OutOfRangeDataStrategy {
None,
Freeze,
Drop
};
private: private:
class InputDevice : public controller::InputDevice { class InputDevice : public controller::InputDevice {
public: public:
InputDevice(vr::IVRSystem*& system); InputDevice(vr::IVRSystem*& system);
bool isHeadControllerMounted() const { return _overrideHead; } bool isHeadControllerMounted() const { return _overrideHead; }
private: private:
// Device functions // Device functions
controller::Input::NamedVector getAvailableInputs() const override; controller::Input::NamedVector getAvailableInputs() const override;
@ -162,6 +169,7 @@ private:
FilteredStick _filteredLeftStick; FilteredStick _filteredLeftStick;
FilteredStick _filteredRightStick; FilteredStick _filteredRightStick;
std::string _headsetName {""}; std::string _headsetName {""};
OutOfRangeDataStrategy _outOfRangeDataStrategy { OutOfRangeDataStrategy::None };
std::vector<PuckPosePair> _validTrackedObjects; std::vector<PuckPosePair> _validTrackedObjects;
std::map<uint32_t, glm::mat4> _pucksPostOffset; std::map<uint32_t, glm::mat4> _pucksPostOffset;