Merge pull request #10729 from druiz17/hand-pucks

Hand pucks and Calibration UI
This commit is contained in:
Seth Alves 2017-06-21 18:10:15 -07:00 committed by GitHub
commit c70365d04a
22 changed files with 1888 additions and 189 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

@ -22,8 +22,8 @@ Original.CheckBox {
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
property bool isRedCheck: false
property int boxSize: 14
property int boxRadius: 3
property bool wrap: true;
readonly property int boxRadius: 3
readonly property int checkSize: Math.max(boxSize - 8, 10)
readonly property int checkRadius: 2
activeFocusOnPress: true

View file

@ -20,6 +20,7 @@ FocusScope {
HifiConstants { id: hifi }
property alias model: comboBox.model;
property alias editable: comboBox.editable
property alias comboBox: comboBox
readonly property alias currentText: comboBox.currentText;
property alias currentIndex: comboBox.currentIndex;

View file

@ -0,0 +1,213 @@
//
// Created by Dante Ruiz on 6/1/17.
// 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
//
import QtQuick 2.5
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import QtQuick.Controls.Styles 1.4
import "../../styles-uit"
import "../../controls"
import "../../controls-uit" as HifiControls
Rectangle {
id: info
signal canceled()
signal restart()
property int count: 3
property string calibratingText: "CALIBRATING..."
property string calibratingCountText: "CALIBRATION STARTING IN"
property string calibrationSuccess: "CALIBRATION COMPLETED"
property string calibrationFailed: "CALIBRATION FAILED"
HifiConstants { id: hifi }
visible: true
color: hifi.colors.baseGray
property string whiteIndicator: "../../../images/loader-calibrate-white.png"
property string blueIndicator: "../../../images/loader-calibrate-blue.png"
Image {
id: busyIndicator
width: 350
height: 350
property bool running: true
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: 60
}
visible: busyIndicator.running
source: blueIndicator
NumberAnimation on rotation {
id: busyRotation
running: busyIndicator.running
loops: Animation.Infinite
duration: 1000
from: 0 ; to: 360
}
}
HiFiGlyphs {
id: image
text: hifi.glyphs.avatar1
size: 190
color: hifi.colors.white
anchors {
top: busyIndicator.top
topMargin: 40
horizontalCenter: busyIndicator.horizontalCenter
}
}
RalewayBold {
id: statusText
text: info.calibratingCountText
size: 16
color: hifi.colors.blueHighlight
anchors {
top: image.bottom
topMargin: 15
horizontalCenter: image.horizontalCenter
}
}
RalewayBold {
id: countDown
text: info.count
color: hifi.colors.blueHighlight
anchors {
top: statusText.bottom
topMargin: 12
horizontalCenter: statusText.horizontalCenter
}
}
RalewayBold {
id: directions
anchors {
top: busyIndicator.bottom
topMargin: 100
horizontalCenter: parent.horizontalCenter
}
color: hifi.colors.white
size: hifi.fontSizes.rootMenuDisclosure
text: "Please stand in a T-Pose during calibration"
}
NumberAnimation {
id: numberAnimation
target: info
property: "count"
to: 0
}
Timer {
id: timer
repeat: false
interval: 3000
onTriggered: {
info.calibrating();
}
}
Timer {
id: closeWindow
repeat: false
interval: 3000
onTriggered: stack.pop();
}
Row {
spacing: 20
anchors {
top: directions.bottom
topMargin: 30
horizontalCenter: parent.horizontalCenter
}
HifiControls.Button {
width: 120
height: 30
color: hifi.buttons.red
text: "RESTART"
onClicked: {
restart();
numberAnimation.stop();
info.count = (timer.interval / 1000);
numberAnimation.start();
timer.restart();
}
}
HifiControls.Button {
width: 120
height: 30
color: hifi.buttons.black
text: "CANCEL"
onClicked: {
canceled();
stack.pop()
}
}
}
function start(interval, countNumber) {
countDown.visible = true;
statusText.color = hifi.colors.blueHighlight;
numberAnimation.duration = interval
info.count = countNumber;
timer.interval = interval;
numberAnimation.start();
timer.start();
}
function calibrating() {
countDown.visible = false;
busyIndicator.source = whiteIndicator;
busyRotation.from = 360
busyRotation.to = 0
statusText.text = info.calibratingText;
statusText.color = hifi.colors.white
}
function success() {
busyIndicator.running = false;
statusText.text = info.calibrationSuccess
statusText.color = hifi.colors.greenHighlight
closeWindow.start();
}
function failure() {
busyIndicator.running = false;
statusText.text = info.calibrationFailed
statusText.color = hifi.colors.redHighlight
closeWindow.start();
}
}

View file

@ -0,0 +1,218 @@
//
// Created by Dante Ruiz on 6/1/17.
// 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
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import "../../styles-uit"
import "../../controls"
import "../../controls-uit" as HifiControls
StackView {
id: stack
initialItem: inputConfiguration
Rectangle {
id: inputConfiguration
anchors.fill: parent
HifiConstants { id: hifi }
color: hifi.colors.baseGray
property var pluginSettings: null
Rectangle {
width: inputConfiguration.width
height: 1
color: hifi.colors.baseGrayShadow
x: -hifi.dimensions.contentMargin.x
}
RalewayRegular {
id: header
text: "Controller Settings"
size: 22
color: "white"
anchors.top: inputConfiguration.top
anchors.left: inputConfiguration.left
anchors.leftMargin: 20
anchors.topMargin: 20
}
Separator {
id: headerSeparator
width: inputConfiguration.width
anchors.top: header.bottom
anchors.topMargin: 10
}
HiFiGlyphs {
id: sourceGlyph
text: hifi.glyphs.source
size: 36
color: hifi.colors.blueHighlight
anchors.top: headerSeparator.bottom
anchors.left: inputConfiguration.left
anchors.leftMargin: 40
anchors.topMargin: 20
}
RalewayRegular {
id: configuration
text: "SELECT DEVICE"
size: 15
color: hifi.colors.lightGrayText
anchors.top: headerSeparator.bottom
anchors.left: sourceGlyph.right
anchors.leftMargin: 10
anchors.topMargin: 30
}
Row {
id: configRow
z: 999
anchors.top: sourceGlyph.bottom
anchors.topMargin: 20
anchors.left: sourceGlyph.left
anchors.leftMargin: 40
spacing: 10
HifiControls.ComboBox {
id: box
width: 160
z: 999
editable: true
colorScheme: hifi.colorSchemes.dark
model: inputPlugins()
label: ""
onCurrentIndexChanged: {
changeSource();
}
}
HifiControls.CheckBox {
id: checkBox
colorScheme: hifi.colorSchemes.dark
text: "show all input devices"
onClicked: {
inputPlugins();
changeSource();
}
}
}
Separator {
id: configurationSeparator
z: 0
width: inputConfiguration.width
anchors.top: configRow.bottom
anchors.topMargin: 10
}
HiFiGlyphs {
id: sliderGlyph
text: hifi.glyphs.sliders
size: 36
color: hifi.colors.blueHighlight
anchors.top: configurationSeparator.bottom
anchors.left: inputConfiguration.left
anchors.leftMargin: 40
anchors.topMargin: 20
}
RalewayRegular {
id: configurationHeader
text: "CONFIGURATION"
size: 15
color: hifi.colors.lightGrayText
anchors.top: configurationSeparator.bottom
anchors.left: sliderGlyph.right
anchors.leftMargin: 10
anchors.topMargin: 30
}
Loader {
id: loader
asynchronous: false
width: inputConfiguration.width
anchors.left: inputConfiguration.left
anchors.right: inputConfiguration.right
anchors.top: configurationHeader.bottom
anchors.topMargin: 10
anchors.bottom: inputConfiguration.bottom
source: InputConfiguration.configurationLayout(box.currentText);
onLoaded: {
if (loader.item.hasOwnProperty("pluginName")) {
if (box.currentText === "Vive") {
loader.item.pluginName = "OpenVR";
} else {
loader.item.pluginName = box.currentText;
}
}
if (loader.item.hasOwnProperty("displayInformation")) {
loader.item.displayConfiguration();
}
}
}
}
function inputPlugins() {
if (checkBox.checked) {
return InputConfiguration.inputPlugins();
} else {
return InputConfiguration.activeInputPlugins();
}
}
function initialize() {
changeSource();
}
function changeSource() {
loader.source = "";
var source = "";
if (box.currentText == "Vive") {
source = InputConfiguration.configurationLayout("OpenVR");
} else {
source = InputConfiguration.configurationLayout(box.currentText);
}
loader.source = source;
if (source === "") {
box.label = "(not configurable)";
} else {
box.label = "";
}
}
Timer {
id: timer
repeat: false
interval: 300
onTriggered: initialize()
}
Component.onCompleted: {
timer.start();
}
}

View file

@ -0,0 +1,879 @@
//
// Created by Dante Ruiz on 6/5/17.
// 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
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import "../../styles-uit"
import "../../controls"
import "../../controls-uit" as HifiControls
import "."
Rectangle {
id: openVrConfiguration
width: parent.width
height: parent.height
anchors.fill: parent
property int leftMargin: 75
property int countDown: 0
property string pluginName: ""
property var displayInformation: null
readonly property bool feetChecked: feetBox.checked
readonly property bool hipsChecked: hipBox.checked
readonly property bool chestChecked: chestBox.checked
readonly property bool shouldersChecked: shoulderBox.checked
readonly property bool hmdHead: headBox.checked
readonly property bool headPuck: headPuckBox.checked
readonly property bool handController: handBox.checked
readonly property bool handPuck: handPuckBox.checked
property int state: buttonState.disabled
property var lastConfiguration: null
HifiConstants { id: hifi }
Component { id: screen; CalibratingScreen {} }
QtObject {
id: buttonState
readonly property int disabled: 0
readonly property int apply: 1
readonly property int applyAndCalibrate: 2
readonly property int calibrate: 3
}
MouseArea {
id: mouseArea
anchors.fill: parent
propagateComposedEvents: true
onPressed: {
parent.forceActiveFocus()
mouse.accepted = false;
}
}
color: hifi.colors.baseGray
RalewayBold {
id: head
text: "Head:"
size: 12
color: "white"
anchors.left: parent.left
anchors.leftMargin: leftMargin
}
Row {
id: headConfig
anchors.top: head.bottom
anchors.topMargin: 5
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.CheckBox {
id: headBox
width: 15
height: 15
boxRadius: 7
onClicked: {
if (checked) {
headPuckBox.checked = false;
} else {
checked = true;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Vive HMD"
color: hifi.colors.lightGrayText
}
HifiControls.CheckBox {
id: headPuckBox
width: 15
height: 15
boxRadius: 7
onClicked: {
if (checked) {
headBox.checked = false;
} else {
checked = true;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Tracker"
color: hifi.colors.lightGrayText
}
}
Row {
id: headOffsets
anchors.top: headConfig.bottom
anchors.topMargin: 5
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
visible: headPuckBox.checked
HifiControls.SpinBox {
id: headYOffset
decimals: 4
width: 112
label: "Y: offset"
minimumValue: -10
stepSize: 0.0254
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
sendConfigurationSettings();
}
}
HifiControls.SpinBox {
id: headZOffset
width: 112
label: "Z: offset"
minimumValue: -10
stepSize: 0.0254
decimals: 4
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
sendConfigurationSettings();
}
}
}
RalewayBold {
id: hands
text: "Hands:"
size: 12
color: "white"
anchors.top: (headOffsets.visible ? headOffsets.bottom : headConfig.bottom)
anchors.topMargin: (headOffsets.visible ? 22 : 10)
anchors.left: parent.left
anchors.leftMargin: leftMargin
}
Row {
id: handConfig
anchors.top: hands.bottom
anchors.topMargin: 5
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.CheckBox {
id: handBox
width: 15
height: 15
boxRadius: 7
onClicked: {
if (checked) {
handPuckBox.checked = false;
} else {
checked = true;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Controllers"
color: hifi.colors.lightGrayText
}
HifiControls.CheckBox {
id: handPuckBox
width: 12
height: 15
boxRadius: 7
onClicked: {
if (checked) {
handBox.checked = false;
} else {
checked = true;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Trackers"
color: hifi.colors.lightGrayText
}
}
Row {
id: handOffset
visible: handPuckBox.checked
anchors.top: handConfig.bottom
anchors.topMargin: 5
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.SpinBox {
id: handYOffset
decimals: 4
width: 112
label: "Y: offset"
minimumValue: -10
stepSize: 0.0254
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
sendConfigurationSettings();
}
}
HifiControls.SpinBox {
id: handZOffset
width: 112
label: "Z: offset"
minimumValue: -10
stepSize: 0.0254
decimals: 4
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
sendConfigurationSettings();
}
}
}
RalewayBold {
id: additional
text: "Additional Trackers"
size: 12
color: hifi.colors.white
anchors.top: (handOffset.visible ? handOffset.bottom : handConfig.bottom)
anchors.topMargin: (handOffset.visible ? 22 : 10)
anchors.left: parent.left
anchors.leftMargin: leftMargin
}
Row {
id: feetConfig
anchors.top: additional.bottom
anchors.topMargin: 15
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.CheckBox {
id: feetBox
width: 15
height: 15
boxRadius: 7
onClicked: {
if (!checked) {
shoulderBox.checked = false;
chestBox.checked = false;
hipBox.checked = false;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Feet"
color: hifi.colors.lightGrayText
}
}
Row {
id: hipConfig
anchors.top: feetConfig.bottom
anchors.topMargin: 15
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.CheckBox {
id: hipBox
width: 15
height: 15
boxRadius: 7
onClicked: {
if (checked) {
feetBox.checked = true;
}
if (chestChecked) {
checked = true;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Hips"
color: hifi.colors.lightGrayText
}
RalewayRegular {
size: 12
text: "requires feet"
color: hifi.colors.lightGray
}
}
Row {
id: chestConfig
anchors.top: hipConfig.bottom
anchors.topMargin: 15
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.CheckBox {
id: chestBox
width: 15
height: 15
boxRadius: 7
onClicked: {
if (checked) {
hipBox.checked = true;
feetBox.checked = true;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Chest"
color: hifi.colors.lightGrayText
}
RalewayRegular {
size: 12
text: "requires hips"
color: hifi.colors.lightGray
}
}
Row {
id: shoulderConfig
anchors.top: chestConfig.bottom
anchors.topMargin: 15
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.CheckBox {
id: shoulderBox
width: 15
height: 15
boxRadius: 7
onClicked: {
if (checked) {
hipBox.checked = true;
feetBox.checked = true;
}
sendConfigurationSettings();
}
}
RalewayBold {
size: 12
text: "Shoulders"
color: hifi.colors.lightGrayText
}
RalewayRegular {
size: 12
text: "requires hips"
color: hifi.colors.lightGray
}
}
Separator {
id: bottomSeperator
width: parent.width
anchors.top: shoulderConfig.bottom
anchors.topMargin: 10
}
Rectangle {
id: calibrationButton
property int color: hifi.buttons.blue
property int colorScheme: hifi.colorSchemes.light
property string glyph: hifi.glyphs.avatar1
property bool enabled: false
property bool pressed: false
property bool hovered: false
property int size: 32
property string text: "apply"
property int padding: 12
width: glyphButton.width + calibrationText.width + padding
height: hifi.dimensions.controlLineHeight
anchors.top: bottomSeperator.bottom
anchors.topMargin: 10
anchors.left: parent.left
anchors.leftMargin: leftMargin
radius: hifi.buttons.radius
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!calibrationButton.enabled) {
hifi.buttons.disabledColorStart[calibrationButton.colorScheme]
} else if (calibrationButton.pressed) {
hifi.buttons.pressedColor[calibrationButton.color]
} else if (calibrationButton.hovered) {
hifi.buttons.hoveredColor[calibrationButton.color]
} else {
hifi.buttons.colorStart[calibrationButton.color]
}
}
}
GradientStop {
position: 1.0
color: {
if (!calibrationButton.enabled) {
hifi.buttons.disabledColorFinish[calibrationButton.colorScheme]
} else if (calibrationButton.pressed) {
hifi.buttons.pressedColor[calibrationButton.color]
} else if (calibrationButton.hovered) {
hifi.buttons.hoveredColor[calibrationButton.color]
} else {
hifi.buttons.colorFinish[calibrationButton.color]
}
}
}
}
HiFiGlyphs {
id: glyphButton
color: enabled ? hifi.buttons.textColor[calibrationButton.color]
: hifi.buttons.disabledTextColor[calibrationButton.colorScheme]
text: calibrationButton.glyph
size: calibrationButton.size
anchors {
top: parent.top
bottom: parent.bottom
bottomMargin: 1
}
}
RalewayBold {
id: calibrationText
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[calibrationButton.color]
: hifi.buttons.disabledTextColor[calibrationButton.colorScheme]
size: hifi.fontSizes.buttonLabel
text: calibrationButton.text
anchors {
left: glyphButton.right
top: parent.top
topMargin: 7
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (calibrationButton.enabled) {
if (openVrConfiguration.state === buttonState.apply) {
InputConfiguration.uncalibratePlugin(pluginName);
updateCalibrationButton();
} else {
calibrationTimer.interval = timeToCalibrate.value * 1000
openVrConfiguration.countDown = timeToCalibrate.value;
var calibratingScreen = screen.createObject();
stack.push(calibratingScreen);
calibratingScreen.canceled.connect(cancelCalibration);
calibratingScreen.restart.connect(restartCalibration);
calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.value);
calibrationTimer.start();
}
}
}
onPressed: {
calibrationButton.pressed = true;
}
onReleased: {
calibrationButton.pressed = false;
}
onEntered: {
calibrationButton.hovered = true;
}
onExited: {
calibrationButton.hovered = false;
}
}
}
Timer {
id: calibrationTimer
repeat: false
interval: 20
onTriggered: {
InputConfiguration.calibratePlugin(pluginName)
}
}
Timer {
id: displayTimer
repeat: false
interval: 3000
onTriggered: {
}
}
Component.onCompleted: {
InputConfiguration.calibrationStatus.connect(calibrationStatusInfo);
lastConfiguration = composeConfigurationSettings();
}
HifiControls.SpinBox {
id: timeToCalibrate
width: 70
anchors.top: calibrationButton.bottom
anchors.topMargin: 40
anchors.left: parent.left
anchors.leftMargin: leftMargin
minimumValue: 3
value: 3
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
calibrationTimer.interval = value * 1000;
openVrConfiguration.countDown = value;
numberAnimation.duration = calibrationTimer.interval;
}
}
RalewayBold {
id: delayTextInfo
size: 10
text: "Delay Before Calibration Starts"
color: hifi.colors.white
anchors {
left: timeToCalibrate.right
leftMargin: 20
verticalCenter: timeToCalibrate.verticalCenter
}
}
RalewayRegular {
size: 12
text: "sec"
color: hifi.colors.lightGray
anchors {
left: delayTextInfo.right
leftMargin: 10
verticalCenter: delayTextInfo.verticalCenter
}
}
NumberAnimation {
id: numberAnimation
target: openVrConfiguration
property: "countDown"
to: 0
}
function calibrationStatusInfo(status) {
var calibrationScreen = stack.currentItem;
if (status["calibrated"]) {
calibrationScreen.success();
} else if (!status["calibrated"]) {
var uncalibrated = status["success"];
if (!uncalibrated) {
calibrationScreen.failure();
}
}
updateCalibrationButton();
}
function trackersForConfiguration() {
var pucksNeeded = 0;
if (headPuckBox.checked) {
pucksNeeded++;
}
if (feetBox.checked) {
pucksNeeded++;
}
if (hipBox.checked) {
pucksNeeded++;
}
if (chestBox.checked) {
pucksNeeded++;
}
if (shoulderBox.checked) {
pucksNeeded++;
}
return pucksNeeded;
}
function cancelCalibration() {
calibrationTimer.stop();
}
function restartCalibration() {
calibrationTimer.restart();
}
function displayConfiguration() {
var settings = InputConfiguration.configurationSettings(pluginName);
var configurationType = settings["trackerConfiguration"];
displayTrackerConfiguration(configurationType);
var HmdHead = settings["HMDHead"];
var viveController = settings["handController"];
if (HmdHead) {
headBox.checked = true;
headPuckBox.checked = false;
} else {
headPuckBox.checked = true;
headBox.checked = false;
}
if (viveController) {
handBox.checked = true;
handPuckBox.checked = false;
} else {
handPuckBox.checked = true;
handBox.checked = false;
}
initializeButtonState();
updateCalibrationText();
}
function displayTrackerConfiguration(type) {
if (type === "Feet") {
feetBox.checked = true;
} else if (type === "FeetAndHips") {
feetBox.checked = true;
hipBox.checked = true;
} else if (type === "FeetHipsChest") {
feetBox.checked = true;
hipBox.checked = true;
chestBox.checked = true;
} else if (type === "FeetHipsAndShoulders") {
feetBox.checked = true;
hipBox.checked = true;
shoulderBox.checked = true;
} else if (type === "FeetHipsChestAndShoulders") {
feetBox.checked = true;
hipBox.checked = true;
chestBox.checked = true;
shoulderBox.checked = true;
}
}
function updateButtonState() {
var settings = composeConfigurationSettings();
var bodySetting = settings["bodyConfiguration"];
var headSetting = settings["headConfiguration"];
var headOverride = headSetting["override"];
var handSetting = settings["handConfiguration"];
var handOverride = handSetting["override"];
var settingsChanged = false;
if (lastConfiguration["bodyConfiguration"] !== bodySetting) {
settingsChanged = true;
}
var lastHead = lastConfiguration["headConfiguration"];
if (lastHead["override"] !== headOverride) {
settingsChanged = true;
}
var lastHand = lastConfiguration["handConfiguration"];
if (lastHand["override"] !== handOverride) {
settingsChanged = true;
}
if (settingsChanged) {
if ((!handOverride) && (!headOverride) && (bodySetting === "None")) {
state = buttonState.apply;
} else {
state = buttonState.applyAndCalibrate;
}
} else {
if (state == buttonState.apply) {
state = buttonState.disabled;
} else if (state == buttonState.applyAndCalibrate) {
state = buttonState.calibrate;
}
}
lastConfiguration = settings;
}
function initializeButtonState() {
var settings = composeConfigurationSettings();
var bodySetting = settings["bodyConfiguration"];
var headSetting = settings["headConfiguration"];
var headOverride = headSetting["override"];
var handSetting = settings["handConfiguration"];
var handOverride = handSetting["override"];
if ((!handOverride) && (!headOverride) && (bodySetting === "None")) {
state = buttonState.disabled;
} else {
state = buttonState.calibrate;
}
}
function updateCalibrationButton() {
updateButtonState();
updateCalibrationText();
}
function updateCalibrationText() {
if (buttonState.disabled == state) {
calibrationButton.enabled = false;
calibrationButton.text = "Apply";
} else if (buttonState.apply == state) {
calibrationButton.enabled = true;
calibrationButton.text = "Apply";
} else if (buttonState.applyAndCalibrate == state) {
calibrationButton.enabled = true;
calibrationButton.text = "Apply And Calibrate";
} else if (buttonState.calibrate == state) {
calibrationButton.enabled = true;
calibrationButton.text = "Calibrate";
}
}
function composeConfigurationSettings() {
var trackerConfiguration = "";
var overrideHead = false;
var overrideHandController = false;
if (shouldersChecked && chestChecked) {
trackerConfiguration = "FeetHipsChestAndShoulders";
} else if (shouldersChecked) {
trackerConfiguration = "FeetHipsAndShoulders";
} else if (chestChecked) {
trackerConfiguration = "FeetHipsAndChest";
} else if (hipsChecked) {
trackerConfiguration = "FeetAndHips";
} else if (feetChecked) {
trackerConfiguration = "Feet";
} else {
trackerConfiguration = "None";
}
if (headPuck) {
overrideHead = true;
} else if (hmdHead) {
overrideHead = false;
}
if (handController) {
overrideHandController = false;
} else if (handPuck) {
overrideHandController = true;
}
var headObject = {
"override": overrideHead,
"Y": headYOffset.value,
"Z": headZOffset.value
}
var handObject = {
"override": overrideHandController,
"Y": handYOffset.value,
"Z": handZOffset.value
}
var settingsObject = {
"bodyConfiguration": trackerConfiguration,
"headConfiguration": headObject,
"handConfiguration": handObject
}
return settingsObject;
}
function sendConfigurationSettings() {
var settings = composeConfigurationSettings();
InputConfiguration.setConfigurationSettings(settings, pluginName);
updateCalibrationButton();
}
}

View file

@ -333,6 +333,7 @@ Item {
readonly property string vol_x_2: "\ue015"
readonly property string vol_x_3: "\ue016"
readonly property string vol_x_4: "\ue017"
readonly property string source: "\ue01c"
readonly property string playback_play: "\ue01d"
readonly property string stop_square: "\ue01e"
}

View file

@ -109,6 +109,7 @@
#include <plugins/PluginManager.h>
#include <plugins/PluginUtils.h>
#include <plugins/SteamClientPlugin.h>
#include <plugins/InputConfiguration.h>
#include <RecordingScriptingInterface.h>
#include <RenderableWebEntityItem.h>
#include <RenderShadowTask.h>
@ -539,6 +540,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<HMDScriptingInterface>();
DependencyManager::set<ResourceScriptingInterface>();
DependencyManager::set<TabletScriptingInterface>();
DependencyManager::set<InputConfiguration>();
DependencyManager::set<ToolbarScriptingInterface>();
DependencyManager::set<UserActivityLoggerScriptingInterface>();
DependencyManager::set<AssetMappingsScriptingInterface>();
@ -2025,6 +2027,7 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("TextureCache", DependencyManager::get<TextureCache>().data());
surfaceContext->setContextProperty("ModelCache", DependencyManager::get<ModelCache>().data());
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
surfaceContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());

View file

@ -19,6 +19,7 @@
#include <AudioClient.h>
#include <CrashHelpers.h>
#include <DependencyManager.h>
#include <TabletScriptingInterface.h>
#include <display-plugins/DisplayPlugin.h>
#include <PathUtils.h>
#include <SettingHandle.h>
@ -37,6 +38,7 @@
#include "MainWindow.h"
#include "render/DrawStatus.h"
#include "scripting/MenuScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
#include "ui/DialogsManager.h"
#include "ui/StandAloneJSConsole.h"
#include "InterfaceLogging.h"
@ -309,6 +311,17 @@ Menu::Menu() {
QString("../../hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
});
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings");
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->loadQMLSource("ControllerSettings.qml");
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();
}
});
// Settings > Control with Speech [advanced]
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
auto speechRecognizer = DependencyManager::get<SpeechRecognizer>();

View file

@ -50,6 +50,7 @@
#include "ui/AvatarInputs.h"
#include "avatar/AvatarManager.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include <plugins/InputConfiguration.h>
#include "ui/Snapshot.h"
#include "SoundCache.h"
@ -182,6 +183,7 @@ void Web3DOverlay::loadSourceURL() {
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto flags = tabletScriptingInterface->getFlags();
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance());
@ -200,6 +202,7 @@ void Web3DOverlay::loadSourceURL() {
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");

View file

@ -0,0 +1,143 @@
//
// Created by Dante Ruiz on 6/1/17.
// 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 "InputConfiguration.h"
#include <QThread>
#include "DisplayPlugin.h"
#include "InputPlugin.h"
#include "PluginManager.h"
InputConfiguration::InputConfiguration() {
}
QStringList InputConfiguration::inputPlugins() {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(this, "inputPlugins", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QStringList, result));
return result;
}
QStringList inputPlugins;
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
QString pluginName = plugin->getName();
if (pluginName == QString("OpenVR")) {
inputPlugins << QString("Vive");
} else {
inputPlugins << pluginName;
}
}
return inputPlugins;
}
QStringList InputConfiguration::activeInputPlugins() {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(this, "activeInputPlugins", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QStringList, result));
return result;
}
QStringList activePlugins;
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
if (plugin->configurable()) {
QString pluginName = plugin->getName();
if (pluginName == QString("OpenVR")) {
activePlugins << QString("Vive");
} else {
activePlugins << pluginName;
}
}
}
return activePlugins;
}
QString InputConfiguration::configurationLayout(QString pluginName) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "configurationLayout", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString, result),
Q_ARG(QString, pluginName));
return result;
}
QString sourcePath;
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
if (plugin->getName() == pluginName) {
return plugin->configurationLayout();
}
}
return sourcePath;
}
void InputConfiguration::setConfigurationSettings(QJsonObject configurationSettings, QString pluginName) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setConfigurationSettings", Qt::BlockingQueuedConnection,
Q_ARG(QJsonObject, configurationSettings),
Q_ARG(QString, pluginName));
return;
}
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
if (plugin->getName() == pluginName) {
plugin->setConfigurationSettings(configurationSettings);
}
}
}
QJsonObject InputConfiguration::configurationSettings(QString pluginName) {
if (QThread::currentThread() != thread()) {
QJsonObject result;
QMetaObject::invokeMethod(this, "configurationSettings", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QJsonObject, result),
Q_ARG(QString, pluginName));
return result;
}
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
if (plugin->getName() == pluginName) {
return plugin->configurationSettings();
}
}
return QJsonObject();
}
void InputConfiguration::calibratePlugin(QString pluginName) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "calibratePlugin", Qt::BlockingQueuedConnection);
return;
}
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
if (plugin->getName() == pluginName) {
plugin->calibrate();
}
}
}
bool InputConfiguration::uncalibratePlugin(QString pluginName) {
if (QThread::currentThread() != thread()) {
bool result;
QMetaObject::invokeMethod(this, "uncalibratePlugin", Qt::BlockingQueuedConnection,
Q_ARG(bool, result));
return result;
}
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
if (plugin->getName() == pluginName) {
return plugin->uncalibrate();
}
}
return false;
}

View file

@ -0,0 +1,37 @@
//
// Created by Dante Ruiz on 6/1/17.
// 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_InputConfiguration_h
#define hifi_InputConfiguration_h
#include <mutex>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QJsonObject>
#include <DependencyManager.h>
class InputConfiguration : public QObject, public Dependency {
Q_OBJECT
public:
InputConfiguration();
Q_INVOKABLE QStringList inputPlugins();
Q_INVOKABLE QStringList activeInputPlugins();
Q_INVOKABLE QString configurationLayout(QString pluginName);
Q_INVOKABLE void setConfigurationSettings(QJsonObject configurationSettings, QString pluginName);
Q_INVOKABLE void calibratePlugin(QString pluginName);
Q_INVOKABLE QJsonObject configurationSettings(QString pluginName);
Q_INVOKABLE bool uncalibratePlugin(QString pluginName);
signals:
void calibrationStatus(const QJsonObject& status);
};
#endif

View file

@ -11,6 +11,7 @@
#pragma once
#include "Plugin.h"
#include <QJsonObject>
namespace controller {
struct InputCalibrationData;
@ -24,7 +25,12 @@ public:
// Some input plugins are comprised of multiple subdevices (SDL2, for instance).
// If an input plugin is only a single device, it will only return it's primary name.
virtual QStringList getSubdeviceNames() { return { getName() }; };
virtual void setConfigurationSettings(const QJsonObject configurationSettings) { }
virtual QJsonObject configurationSettings() { return QJsonObject(); }
virtual QString configurationLayout() { return QString(); }
virtual void calibrate() {}
virtual bool uncalibrate() { return false; }
virtual bool configurable() { return false; }
virtual bool isHandController() const { return false; }
virtual bool isHeadController() const { return false; }
};

View file

@ -8,7 +8,7 @@
set(TARGET_NAME hifiCodec)
setup_hifi_client_server_plugin()
link_hifi_libraries(audio plugins)
link_hifi_libraries(audio plugins input-plugins display-plugins)
add_dependency_external_projects(hifiAudioCodec)
target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES})

View file

@ -11,7 +11,7 @@ if (WIN32)
if (KINECT_FOUND)
set(TARGET_NAME hifiKinect)
setup_hifi_plugin(Script Qml Widgets)
link_hifi_libraries(shared controllers ui plugins input-plugins)
link_hifi_libraries(shared controllers ui plugins input-plugins display-plugins)
# need to setup appropriate externals...
target_kinect()

View file

@ -10,7 +10,7 @@ if (APPLE OR WIN32)
set(TARGET_NAME hifiNeuron)
setup_hifi_plugin(Script Qml Widgets)
link_hifi_libraries(shared controllers ui plugins input-plugins)
link_hifi_libraries(shared controllers ui plugins input-plugins display-plugins)
target_neuron()
endif()

View file

@ -8,5 +8,5 @@
set(TARGET_NAME hifiSdl2)
setup_hifi_plugin(Script Qml Widgets)
link_hifi_libraries(shared controllers ui plugins input-plugins script-engine)
link_hifi_libraries(shared controllers ui plugins input-plugins script-engine display-plugins)
target_sdl2()

View file

@ -9,6 +9,6 @@
if (NOT ANDROID)
set(TARGET_NAME hifiSixense)
setup_hifi_plugin(Script Qml Widgets)
link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins)
link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins display-plugins)
target_sixense()
endif ()

View file

@ -30,7 +30,7 @@
#include <glm/gtc/quaternion.hpp>
#include <controllers/UserInputMapper.h>
#include <Plugins/InputConfiguration.h>
#include <controllers/StandardControls.h>
@ -39,7 +39,7 @@ extern PoseData _nextSimPoseData;
vr::IVRSystem* acquireOpenVrSystem();
void releaseOpenVrSystem();
static const QString OPENVR_LAYOUT = QString("OpenVrConfiguration.qml");
static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND;
@ -47,18 +47,23 @@ static const char* MENU_PARENT = "Avatar";
static const char* MENU_NAME = "Vive Controllers";
static const char* MENU_PATH = "Avatar" ">" "Vive Controllers";
static const char* RENDER_CONTROLLERS = "Render Hand Controllers";
static const int MIN_HEAD = 1;
static const int MIN_PUCK_COUNT = 2;
static const int MIN_FEET_AND_HIPS = 3;
static const int MIN_FEET_HIPS_CHEST = 4;
static const int MIN_FEET_HIPS_HEAD = 4;
static const int MIN_FEET_HIPS_SHOULDERS = 5;
static const int MIN_FEET_HIPS_CHEST_HEAD = 5;
static const int MIN_FEET_HIPS_CHEST_SHOULDERS = 6;
static const int FIRST_FOOT = 0;
static const int SECOND_FOOT = 1;
static const int HIP = 2;
static const int CHEST = 3;
static float HEAD_PUCK_Y_OFFSET = -0.0254f;
static float HEAD_PUCK_Z_OFFSET = -0.152f;
static float HAND_PUCK_Y_OFFSET = -0.0508f;
static float HAND_PUCK_Z_OFFSET = 0.0254f;
const char* ViveControllerManager::NAME { "OpenVR" };
@ -84,7 +89,7 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck)
return (firstPuck.second.translation.x < secondPuck.second.translation.x);
}
static bool determineFeetOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) {
static bool determineLimbOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) {
glm::vec3 poseAPosition = poseA.getTranslation();
glm::vec3 poseBPosition = poseB.getTranslation();
@ -116,10 +121,42 @@ static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult)
return result;
}
void ViveControllerManager::calibrate() {
if (isSupported()) {
_inputDevice->calibrateNextFrame();
}
}
bool ViveControllerManager::uncalibrate() {
if (isSupported()) {
_inputDevice->uncalibrate();
return true;
}
return false;
}
bool ViveControllerManager::isSupported() const {
return openVrSupported();
}
void ViveControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) {
if (isSupported()) {
_inputDevice->configureCalibrationSettings(configurationSettings);
}
}
QJsonObject ViveControllerManager::configurationSettings() {
if (isSupported()) {
return _inputDevice->configurationSettings();
}
return QJsonObject();
}
QString ViveControllerManager::configurationLayout() {
return OPENVR_LAYOUT;
}
bool ViveControllerManager::activate() {
InputPlugin::activate();
@ -207,17 +244,11 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu
ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) {
_configStringMap[Config::Auto] = QString("Auto");
_configStringMap[Config::Feet] = QString("Feet");
_configStringMap[Config::FeetAndHips] = QString("FeetAndHips");
_configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest");
_configStringMap[Config::None] = QString("None");
_configStringMap[Config::Feet] = QString("Feet");
_configStringMap[Config::FeetAndHips] = QString("FeetAndHips");
_configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest");
_configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders");
_configStringMap[Config::FeetHipsChestAndHead] = QString("FeetHipsChestAndHead");
_configStringMap[Config::FeetHipsAndHead] = QString("FeetHipsAndHead");
if (openVrSupported()) {
createPreferences();
}
}
void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
@ -265,6 +296,14 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle
}
_trackedControllers = numTrackedControllers;
calibrateFromHandController(inputCalibrationData);
calibrateFromUI(inputCalibrationData);
updateCalibratedLimbs();
_lastSimPoseData = _nextSimPoseData;
}
void ViveControllerManager::InputDevice::calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData) {
if (checkForCalibrationEvent()) {
quint64 currentTime = usecTimestampNow();
if (!_timeTilCalibrationSet) {
@ -280,9 +319,82 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle
_triggersPressedHandled = false;
_timeTilCalibrationSet = false;
}
}
updateCalibratedLimbs();
_lastSimPoseData = _nextSimPoseData;
void ViveControllerManager::InputDevice::calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData) {
if (_calibrate) {
uncalibrate();
calibrate(inputCalibrationData);
_calibrate = false;
}
}
void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJsonObject configurationSettings) {
Locker locker(_lock);
if (!configurationSettings.empty()) {
auto iter = configurationSettings.begin();
auto end = configurationSettings.end();
while (iter != end) {
if (iter.key() == "bodyConfiguration") {
setConfigFromString(iter.value().toString());
} else if (iter.key() == "headConfiguration") {
QJsonObject headObject = iter.value().toObject();
bool overrideHead = headObject["override"].toBool();
if (overrideHead) {
_headConfig = HeadConfig::Puck;
HEAD_PUCK_Y_OFFSET = headObject["Y"].toDouble();
HEAD_PUCK_Z_OFFSET = headObject["Z"].toDouble();
} else {
_headConfig = HeadConfig::HMD;
}
} else if (iter.key() == "handConfiguration") {
QJsonObject handsObject = iter.value().toObject();
bool overrideHands = handsObject["override"].toBool();
if (overrideHands) {
_handConfig = HandConfig::Pucks;
HAND_PUCK_Y_OFFSET = handsObject["Y"].toDouble();
HAND_PUCK_Z_OFFSET = handsObject["Z"].toDouble();
} else {
_handConfig = HandConfig::HandController;
}
}
iter++;
}
}
}
void ViveControllerManager::InputDevice::calibrateNextFrame() {
Locker locker(_lock);
_calibrate = true;
}
QJsonObject ViveControllerManager::InputDevice::configurationSettings() {
Locker locker(_lock);
QJsonObject configurationSettings;
configurationSettings["trackerConfiguration"] = configToString(_preferedConfig);
configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD) ? true : false;
configurationSettings["handController"] = (_handConfig == HandConfig::HandController) ? true : false;
return configurationSettings;
}
void ViveControllerManager::InputDevice::emitCalibrationStatus(const bool success) {
auto inputConfiguration = DependencyManager::get<InputConfiguration>();
QJsonObject status = QJsonObject();
if (_calibrated && success) {
status["calibrated"] = _calibrated;
status["configuration"] = configToString(_preferedConfig);
} else if (!_calibrated && !success) {
status["calibrated"] = _calibrated;
status["success"] = success;
} else if (!_calibrated && success) {
status["calibrated"] = _calibrated;
status["success"] = success;
status["configuration"] = configToString(_preferedConfig);
status["puckCount"] = (int)_validTrackedObjects.size();
}
emit inputConfiguration->calibrationStatus(status); //inputConfiguration->calibrated(status);
}
void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) {
@ -330,102 +442,142 @@ void ViveControllerManager::InputDevice::calibrateOrUncalibrate(const controller
calibrate(inputCalibration);
} else {
uncalibrate();
emitCalibrationStatus(true);
}
}
void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibrationData& inputCalibration) {
qDebug() << "Puck Calibration: Starting...";
// convert the hmd head from sensor space to avatar space
glm::mat4 hmdSensorFlippedMat = inputCalibration.hmdSensorMat * Matrices::Y_180;
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
glm::mat4 hmdAvatarMat = sensorToAvatarMat * hmdSensorFlippedMat;
// cancel the roll and pitch for the hmd head
glm::quat hmdRotation = cancelOutRollAndPitch(glmExtractRotation(hmdAvatarMat));
glm::vec3 hmdTranslation = extractTranslation(hmdAvatarMat);
glm::mat4 currentHmd = createMatFromQuatAndPos(hmdRotation, hmdTranslation);
// calculate the offset from the centerOfEye to defaultHeadMat
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
glm::mat4 currentHead = currentHmd * defaultHeadOffset;
// calculate the defaultToRefrenceXform
glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
int puckCount = (int)_validTrackedObjects.size();
qDebug() << "Puck Calibration: " << puckCount << " pucks found for calibration";
_config = _preferedConfig;
if (_config != Config::Auto && puckCount < MIN_PUCK_COUNT) {
qDebug() << "Puck Calibration: Failed: Could not meet the minimal # of pucks";
if (puckCount == 0) {
uncalibrate();
emitCalibrationStatus(false);
return;
} else if (_config == Config::Auto){
if (puckCount == MIN_PUCK_COUNT) {
_config = Config::Feet;
qDebug() << "Puck Calibration: Auto Config: " << configToString(_config) << " configuration";
} else if (puckCount == MIN_FEET_AND_HIPS) {
_config = Config::FeetAndHips;
qDebug() << "Puck Calibration: Auto Config: " << configToString(_config) << " configuration";
} else if (puckCount >= MIN_FEET_HIPS_CHEST) {
_config = Config::FeetHipsAndChest;
qDebug() << "Puck Calibration: Auto Config: " << configToString(_config) << " configuration";
} else {
qDebug() << "Puck Calibration: Auto Config Failed: Could not meet the minimal # of pucks";
uncalibrate();
return;
}
}
std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition);
glm::mat4 defaultToReferenceMat = glm::mat4();
if (_headConfig == HeadConfig::HMD) {
defaultToReferenceMat = calculateDefaultToReferenceForHmd(inputCalibration);
} else if (_headConfig == HeadConfig::Puck) {
defaultToReferenceMat = calculateDefaultToReferenceForHeadPuck(inputCalibration);
}
_config = _preferedConfig;
bool headConfigured = configureHead(defaultToReferenceMat, inputCalibration);
bool handsConfigured = configureHands(defaultToReferenceMat, inputCalibration);
bool bodyConfigured = configureBody(defaultToReferenceMat, inputCalibration);
if (_config == Config::Feet) {
if (!headConfigured || !handsConfigured || !bodyConfigured) {
uncalibrate();
emitCalibrationStatus(false);
} else {
_calibrated = true;
emitCalibrationStatus(true);
qDebug() << "PuckCalibration: " << configToString(_config) << " Configuration Successful";
}
}
bool ViveControllerManager::InputDevice::configureHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksXPosition);
int puckCount = (int)_validTrackedObjects.size();
if (_handConfig == HandConfig::Pucks && puckCount >= MIN_PUCK_COUNT) {
glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat);
glm::vec3 headPosition = getReferenceHeadPosition(defaultToReferenceMat, inputCalibration.defaultHeadMat);
size_t FIRST_INDEX = 0;
size_t LAST_INDEX = _validTrackedObjects.size() -1;
auto& firstHand = _validTrackedObjects[FIRST_INDEX];
auto& secondHand = _validTrackedObjects[LAST_INDEX];
controller::Pose& firstHandPose = firstHand.second;
controller::Pose& secondHandPose = secondHand.second;
if (determineLimbOrdering(firstHandPose, secondHandPose, headXAxis, headPosition)) {
calibrateLeftHand(defaultToReferenceMat, inputCalibration, firstHand);
calibrateRightHand(defaultToReferenceMat, inputCalibration, secondHand);
_validTrackedObjects.erase(_validTrackedObjects.begin());
_validTrackedObjects.erase(_validTrackedObjects.end() - 1);
_overrideHands = true;
return true;
} else {
calibrateLeftHand(defaultToReferenceMat, inputCalibration, secondHand);
calibrateRightHand(defaultToReferenceMat, inputCalibration, firstHand);
_validTrackedObjects.erase(_validTrackedObjects.begin());
_validTrackedObjects.erase(_validTrackedObjects.end() - 1);
_overrideHands = true;
return true;
}
} else if (_handConfig == HandConfig::HandController) {
_overrideHands = false;
return true;
}
return false;
}
bool ViveControllerManager::InputDevice::configureHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition);
int puckCount = (int)_validTrackedObjects.size();
if (_headConfig == HeadConfig::Puck && puckCount >= MIN_HEAD) {
calibrateHead(defaultToReferenceMat, inputCalibration);
_validTrackedObjects.erase(_validTrackedObjects.end() - 1);
_overrideHead = true;
return true;
} else if (_headConfig == HeadConfig::HMD) {
return true;
}
return false;
}
bool ViveControllerManager::InputDevice::configureBody(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition);
int puckCount = (int)_validTrackedObjects.size();
glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat);
glm::vec3 headPosition = getReferenceHeadPosition(defaultToReferenceMat, inputCalibration.defaultHeadMat);
if (_config == Config::None) {
return true;
} else if (_config == Config::Feet && puckCount >= MIN_PUCK_COUNT) {
calibrateFeet(defaultToReferenceMat, inputCalibration);
return true;
} else if (_config == Config::FeetAndHips && puckCount >= MIN_FEET_AND_HIPS) {
calibrateFeet(defaultToReferenceMat, inputCalibration);
calibrateHips(defaultToReferenceMat, inputCalibration);
return true;
} else if (_config == Config::FeetHipsAndChest && puckCount >= MIN_FEET_HIPS_CHEST) {
calibrateFeet(defaultToReferenceMat, inputCalibration);
calibrateHips(defaultToReferenceMat, inputCalibration);
calibrateChest(defaultToReferenceMat, inputCalibration);
return true;
} else if (_config == Config::FeetHipsAndShoulders && puckCount >= MIN_FEET_HIPS_SHOULDERS) {
calibrateFeet(defaultToReferenceMat, inputCalibration);
calibrateHips(defaultToReferenceMat, inputCalibration);
int firstShoulderIndex = 3;
int secondShoulderIndex = 4;
calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex);
} else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) {
glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
_overrideHead = true;
} else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) {
glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
calibrateChest(headPuckDefaultToReferenceMat, inputCalibration);
calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
_overrideHead = true;
} else {
qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
uncalibrate();
return;
return true;
} else if (_config == Config::FeetHipsChestAndShoulders && puckCount >= MIN_FEET_HIPS_CHEST_SHOULDERS) {
calibrateFeet(defaultToReferenceMat, inputCalibration);
calibrateHips(defaultToReferenceMat, inputCalibration);
calibrateChest(defaultToReferenceMat, inputCalibration);
int firstShoulderIndex = 4;
int secondShoulderIndex = 5;
calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex);
return true;
}
_calibrated = true;
qDebug() << "PuckCalibration: " << configToString(_config) << " Configuration Successful";
qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
uncalibrate();
emitCalibrationStatus(false);
return false;
}
void ViveControllerManager::InputDevice::uncalibrate() {
_config = Config::Auto;
_config = Config::None;
_pucksOffset.clear();
_jointToPuckMap.clear();
_calibrated = false;
_overrideHead = false;
_overrideHands = false;
}
void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
@ -439,6 +591,11 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
if (_overrideHead) {
_poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD);
}
if (_overrideHands) {
_poseStateMap[controller::LEFT_HAND] = addOffsetToPuckPose(controller::LEFT_HAND);
_poseStateMap[controller::RIGHT_HAND] = addOffsetToPuckPose(controller::RIGHT_HAND);
}
}
controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const {
@ -503,8 +660,29 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
}
}
}
glm::mat4 ViveControllerManager::InputDevice::calculateDefaultToReferenceForHmd(const controller::InputCalibrationData& inputCalibration) {
// convert the hmd head from sensor space to avatar space
glm::mat4 hmdSensorFlippedMat = inputCalibration.hmdSensorMat * Matrices::Y_180;
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
glm::mat4 hmdAvatarMat = sensorToAvatarMat * hmdSensorFlippedMat;
glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) {
// cancel the roll and pitch for the hmd head
glm::quat hmdRotation = cancelOutRollAndPitch(glmExtractRotation(hmdAvatarMat));
glm::vec3 hmdTranslation = extractTranslation(hmdAvatarMat);
glm::mat4 currentHmd = createMatFromQuatAndPos(hmdRotation, hmdTranslation);
// calculate the offset from the centerOfEye to defaultHeadMat
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
glm::mat4 currentHead = currentHmd * defaultHeadOffset;
// calculate the defaultToRefrenceXform
glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
return defaultToReferenceMat;
}
glm::mat4 ViveControllerManager::InputDevice::calculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) {
glm::mat4 avatarToSensorMat = glm::inverse(inputCalibration.sensorToWorldMat) * inputCalibration.avatarMat;
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
size_t headPuckIndex = _validTrackedObjects.size() - 1;
@ -709,33 +887,78 @@ void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool lef
}
}
void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
controller::Pose& firstFootPose = firstFoot.second;
controller::Pose& secondFootPose = secondFoot.second;
if (firstFootPose.translation.x < secondFootPose.translation.x) {
_jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first;
_pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose);
_jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first;
_pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose);
} else {
_jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first;
_pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose);
_jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first;
_pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose);
void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) {
controller::Pose& handPose = handPair.second;
glm::mat4 handPoseAvatarMat = createMatFromQuatAndPos(handPose.getRotation(), handPose.getTranslation());
glm::vec3 handPoseTranslation = extractTranslation(handPoseAvatarMat);
glm::vec3 handPoseZAxis = glmExtractRotation(handPoseAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 avatarHandYAxis = transformVectorFast(inputCalibration.defaultLeftHand, glm::vec3(0.0f, 1.0f, 0.0f));
const float EPSILON = 1.0e-4f;
if (fabsf(fabsf(glm::dot(glm::normalize(avatarHandYAxis), glm::normalize(handPoseZAxis))) - 1.0f) < EPSILON) {
handPoseZAxis = glm::vec3(0.0f, 0.0f, 1.0f);
}
glm::vec3 zPrime = handPoseZAxis;
glm::vec3 xPrime = glm::normalize(glm::cross(avatarHandYAxis, handPoseZAxis));
glm::vec3 yPrime = glm::normalize(glm::cross(zPrime, xPrime));
glm::mat4 newHandMat = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f),
glm::vec4(zPrime, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
glm::vec3 translationOffset = glm::vec3(0.0f, HAND_PUCK_Y_OFFSET, HAND_PUCK_Z_OFFSET);
glm::quat initialRotation = glmExtractRotation(handPoseAvatarMat);
glm::quat finalRotation = glmExtractRotation(newHandMat);
glm::quat rotationOffset = glm::inverse(initialRotation) * finalRotation;
glm::mat4 offsetMat = createMatFromQuatAndPos(rotationOffset, translationOffset);
_jointToPuckMap[controller::LEFT_HAND] = handPair.first;
_pucksOffset[handPair.first] = offsetMat;
}
void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) {
controller::Pose& handPose = handPair.second;
glm::mat4 handPoseAvatarMat = createMatFromQuatAndPos(handPose.getRotation(), handPose.getTranslation());
glm::vec3 handPoseTranslation = extractTranslation(handPoseAvatarMat);
glm::vec3 handPoseZAxis = glmExtractRotation(handPoseAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 avatarHandYAxis = transformVectorFast(inputCalibration.defaultRightHand, glm::vec3(0.0f, 1.0f, 0.0f));
const float EPSILON = 1.0e-4f;
if (fabsf(fabsf(glm::dot(glm::normalize(avatarHandYAxis), glm::normalize(handPoseZAxis))) - 1.0f) < EPSILON) {
handPoseZAxis = glm::vec3(0.0f, 0.0f, 1.0f);
}
glm::vec3 zPrime = handPoseZAxis;
glm::vec3 xPrime = glm::normalize(glm::cross(avatarHandYAxis, handPoseZAxis));
glm::vec3 yPrime = glm::normalize(glm::cross(zPrime, xPrime));
glm::mat4 newHandMat = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f),
glm::vec4(zPrime, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
glm::vec3 translationOffset = glm::vec3(0.0f, HAND_PUCK_Y_OFFSET, HAND_PUCK_Z_OFFSET);
glm::quat initialRotation = glmExtractRotation(handPoseAvatarMat);
glm::quat finalRotation = glmExtractRotation(newHandMat);
glm::quat rotationOffset = glm::inverse(initialRotation) * finalRotation;
glm::mat4 offsetMat = createMatFromQuatAndPos(rotationOffset, translationOffset);
_jointToPuckMap[controller::RIGHT_HAND] = handPair.first;
_pucksOffset[handPair.first] = offsetMat;
}
void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition) {
void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat);
glm::vec3 headPosition = getReferenceHeadPosition(defaultToReferenceMat, inputCalibration.defaultHeadMat);
auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
controller::Pose& firstFootPose = firstFoot.second;
controller::Pose& secondFootPose = secondFoot.second;
if (determineFeetOrdering(firstFootPose, secondFootPose, headXAxis, headPosition)) {
if (determineLimbOrdering(firstFootPose, secondFootPose, headXAxis, headPosition)) {
_jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first;
_pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose);
_jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first;
@ -790,32 +1013,13 @@ void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToRefer
_pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead);
}
void ViveControllerManager::InputDevice::loadSettings() {
Settings settings;
settings.beginGroup("PUCK_CONFIG");
{
_preferedConfig = (Config)settings.value("configuration", QVariant((int)Config::Auto)).toInt();
}
settings.endGroup();
}
void ViveControllerManager::InputDevice::saveSettings() const {
Settings settings;
settings.beginGroup("PUCK_CONFIG");
{
settings.setValue(QString("configuration"), (int)_preferedConfig);
}
settings.endGroup();
}
QString ViveControllerManager::InputDevice::configToString(Config config) {
return _configStringMap[config];
}
void ViveControllerManager::InputDevice::setConfigFromString(const QString& value) {
if (value == "Auto") {
_preferedConfig = Config::Auto;
if (value == "None") {
_preferedConfig = Config::None;
} else if (value == "Feet") {
_preferedConfig = Config::Feet;
} else if (value == "FeetAndHips") {
@ -824,58 +1028,8 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
_preferedConfig = Config::FeetHipsAndChest;
} else if (value == "FeetHipsAndShoulders") {
_preferedConfig = Config::FeetHipsAndShoulders;
} else if (value == "FeetHipsChestAndHead") {
_preferedConfig = Config::FeetHipsChestAndHead;
} else if (value == "FeetHipsAndHead") {
_preferedConfig = Config::FeetHipsAndHead;
}
}
void ViveControllerManager::InputDevice::createPreferences() {
loadSettings();
auto preferences = DependencyManager::get<Preferences>();
static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration";
{
static const float MIN_VALUE = -3.0f;
static const float MAX_VALUE = 3.0f;
static const float STEP = 0.01f;
auto getter = [this]()->float { return HEAD_PUCK_Y_OFFSET; };
auto setter = [this](const float& value) { HEAD_PUCK_Y_OFFSET = value; };
auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckYOffset", getter, setter);
preference->setMin(MIN_VALUE);
preference->setMax(MAX_VALUE);
preference->setDecimals(3);
preference->setStep(STEP);
preferences->addPreference(preference);
}
{
static const float MIN_VALUE = -3.0f;
static const float MAX_VALUE = 3.0f;
static const float STEP = 0.01f;
auto getter = [this]()->float { return HEAD_PUCK_Z_OFFSET; };
auto setter = [this](const float& value) { HEAD_PUCK_Z_OFFSET = value; };
auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckXOffset", getter, setter);
preference->setMin(MIN_VALUE);
preference->setMax(MAX_VALUE);
preference->setStep(STEP);
preference->setDecimals(3);
preferences->addPreference(preference);
}
{
auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; };
auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); };
auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter);
QStringList list = {"Auto", "Feet", "FeetAndHips", "FeetHipsAndChest", "FeetHipsAndShoulders", "FeetHipsAndHead"};
preference->setItems(list);
preferences->addPreference(preference);
} else if (value == "FeetHipsChestAndShoulders") {
_preferedConfig = Config::FeetHipsChestAndShoulders;
}
}

View file

@ -39,6 +39,13 @@ public:
const QString getName() const override { return NAME; }
bool isHandController() const override { return true; }
bool configurable() override { return true; }
QString configurationLayout() override;
void setConfigurationSettings(const QJsonObject configurationSettings) override;
QJsonObject configurationSettings() override;
void calibrate() override;
bool uncalibrate() override;
bool isHeadController() const override { return true; }
bool isHeadControllerMounted() const;
@ -61,14 +68,16 @@ private:
QString getDefaultMappingConfig() const override;
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
void focusOutEvent() override;
void createPreferences();
bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override;
void hapticsHelper(float deltaTime, bool leftHand);
void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration);
void calibrate(const controller::InputCalibrationData& inputCalibration);
void uncalibrate();
void configureCalibrationSettings(const QJsonObject configurationSettings);
QJsonObject configurationSettings();
controller::Pose addOffsetToPuckPose(int joint) const;
glm::mat4 recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration);
glm::mat4 calculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration);
glm::mat4 calculateDefaultToReferenceForHmd(const controller::InputCalibrationData& inputCalibration);
void updateCalibratedLimbs();
bool checkForCalibrationEvent();
void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
@ -83,16 +92,21 @@ private:
void partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPsuedoButton, int xPseudoButton, int yPseudoButton);
void printDeviceTrackingResultChange(uint32_t deviceIndex);
void setConfigFromString(const QString& value);
void loadSettings();
void saveSettings() const;
bool configureHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
bool configureHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
bool configureBody(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateLeftHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair);
void calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair);
void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition);
void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
int firstShoulderIndex, int secondShoulderIndex);
void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData);
void calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData);
void emitCalibrationStatus(const bool success);
void calibrateNextFrame();
class FilteredStick {
@ -119,16 +133,28 @@ private:
glm::vec2 _stick { 0.0f, 0.0f };
};
enum class Config {
Auto,
None,
Feet,
FeetAndHips,
FeetHipsAndChest,
FeetHipsAndShoulders,
FeetHipsChestAndHead,
FeetHipsAndHead
FeetHipsChestAndShoulders,
};
Config _config { Config::Auto };
Config _preferedConfig { Config::Auto };
enum class HeadConfig {
HMD,
Puck
};
enum class HandConfig {
HandController,
Pucks
};
Config _config { Config::None };
Config _preferedConfig { Config::None };
HeadConfig _headConfig { HeadConfig::HMD };
HandConfig _handConfig { HandConfig::HandController };
FilteredStick _filteredLeftStick;
FilteredStick _filteredRightStick;
@ -152,7 +178,9 @@ private:
bool _triggersPressedHandled { false };
bool _calibrated { false };
bool _timeTilCalibrationSet { false };
bool _calibrate { false };
bool _overrideHead { false };
bool _overrideHands { false };
mutable std::recursive_mutex _lock;
QString configToString(Config config);

View file

@ -8,6 +8,6 @@
set(TARGET_NAME pcmCodec)
setup_hifi_client_server_plugin()
link_hifi_libraries(shared plugins)
link_hifi_libraries(shared plugins input-plugins display-plugins)
install_beside_console()