mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 04:24:47 +02:00
Merge remote-tracking branch 'highfidelity/master'
This commit is contained in:
commit
47d3f418e3
30 changed files with 362 additions and 679 deletions
|
@ -21,7 +21,7 @@ var MINIMUM_DEPTH_ADJUST = 0.01;
|
||||||
var NON_LINEAR_DIVISOR = 2;
|
var NON_LINEAR_DIVISOR = 2;
|
||||||
var MINIMUM_SEEK_DISTANCE = 0.01;
|
var MINIMUM_SEEK_DISTANCE = 0.01;
|
||||||
|
|
||||||
var lastMouseMove = Date.now();
|
var lastMouseMoveOrClick = Date.now();
|
||||||
var lastMouseX = Reticle.position.x;
|
var lastMouseX = Reticle.position.x;
|
||||||
var lastMouseY = Reticle.position.y;
|
var lastMouseY = Reticle.position.y;
|
||||||
var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds
|
var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds
|
||||||
|
@ -32,6 +32,14 @@ var WEIGHTING = 1/20; // simple moving average over last 20 samples
|
||||||
var ONE_MINUS_WEIGHTING = 1 - WEIGHTING;
|
var ONE_MINUS_WEIGHTING = 1 - WEIGHTING;
|
||||||
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50;
|
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50;
|
||||||
|
|
||||||
|
function showReticleOnMouseClick() {
|
||||||
|
Reticle.visible = true;
|
||||||
|
lastMouseMoveOrClick = Date.now(); // move or click
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.mousePressEvent.connect(showReticleOnMouseClick);
|
||||||
|
Controller.mouseDoublePressEvent.connect(showReticleOnMouseClick);
|
||||||
|
|
||||||
Controller.mouseMoveEvent.connect(function(mouseEvent) {
|
Controller.mouseMoveEvent.connect(function(mouseEvent) {
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
|
||||||
|
@ -47,7 +55,7 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) {
|
||||||
if (HMD.active && !shouldSeekToLookAt && Reticle.allowMouseCapture) {
|
if (HMD.active && !shouldSeekToLookAt && Reticle.allowMouseCapture) {
|
||||||
var dx = Reticle.position.x - lastMouseX;
|
var dx = Reticle.position.x - lastMouseX;
|
||||||
var dy = Reticle.position.y - lastMouseY;
|
var dy = Reticle.position.y - lastMouseY;
|
||||||
var dt = Math.max(1, (now - lastMouseMove)); // mSecs since last mouse move
|
var dt = Math.max(1, (now - lastMouseMoveOrClick)); // mSecs since last mouse move
|
||||||
var mouseMoveDistance = Math.sqrt((dx*dx) + (dy*dy));
|
var mouseMoveDistance = Math.sqrt((dx*dx) + (dy*dy));
|
||||||
var mouseVelocity = mouseMoveDistance / dt;
|
var mouseVelocity = mouseMoveDistance / dt;
|
||||||
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * mouseVelocity);
|
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * mouseVelocity);
|
||||||
|
@ -56,7 +64,7 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastMouseMove = now;
|
lastMouseMoveOrClick = now;
|
||||||
lastMouseX = mouseEvent.x;
|
lastMouseX = mouseEvent.x;
|
||||||
lastMouseY = mouseEvent.y;
|
lastMouseY = mouseEvent.y;
|
||||||
});
|
});
|
||||||
|
@ -94,7 +102,7 @@ function autoHideReticle() {
|
||||||
// system overlay (like a window), then hide the reticle
|
// system overlay (like a window), then hide the reticle
|
||||||
if (Reticle.visible && !Reticle.pointingAtSystemOverlay) {
|
if (Reticle.visible && !Reticle.pointingAtSystemOverlay) {
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
var timeSinceLastMouseMove = now - lastMouseMove;
|
var timeSinceLastMouseMove = now - lastMouseMoveOrClick;
|
||||||
if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {
|
if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {
|
||||||
Reticle.visible = false;
|
Reticle.visible = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
//
|
||||||
|
// MessageDialog.qml
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 18 Jan 2016
|
||||||
|
// Copyright 2016 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 QtQuick.Controls 1.4
|
||||||
|
|
||||||
|
@ -94,8 +104,8 @@ Item {
|
||||||
|
|
||||||
function buildMenu(items, targetPosition) {
|
function buildMenu(items, targetPosition) {
|
||||||
var model = toModel(items);
|
var model = toModel(items);
|
||||||
// Menu's must be childed to desktop for Z-ordering
|
// Menus must be childed to desktop for Z-ordering
|
||||||
var newMenu = menuViewMaker.createObject(desktop, { model: model, z: topMenu ? topMenu.z + 1 : desktop.zLevels.menu });
|
var newMenu = menuViewMaker.createObject(desktop, { model: model, z: topMenu ? topMenu.z + 1 : desktop.zLevels.menu, isSubMenu: topMenu !== null });
|
||||||
if (targetPosition) {
|
if (targetPosition) {
|
||||||
newMenu.x = targetPosition.x
|
newMenu.x = targetPosition.x
|
||||||
newMenu.y = targetPosition.y - newMenu.height / 3 * 1
|
newMenu.y = targetPosition.y - newMenu.height / 3 * 1
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
import QtQuick 2.4
|
//
|
||||||
import QtQuick.Controls 1.3
|
// VrMenuItem.qml
|
||||||
import QtQuick.Controls.Styles 1.3
|
//
|
||||||
|
// Created by Bradley Austin Davis on 29 Apr 2015
|
||||||
|
// Copyright 2015 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 "../controls"
|
import QtQuick 2.5
|
||||||
import "../styles"
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
import "../controls-uit"
|
||||||
|
import "../styles-uit"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
@ -11,54 +21,93 @@ Item {
|
||||||
property alias text: label.text
|
property alias text: label.text
|
||||||
property var source
|
property var source
|
||||||
|
|
||||||
implicitHeight: source.visible ? label.implicitHeight * 1.5 : 0
|
implicitHeight: source.visible ? 2 * label.implicitHeight : 0
|
||||||
implicitWidth: label.width + label.height * 2.5
|
implicitWidth: 2 * hifi.dimensions.menuPadding.x + check.width + label.width + tail.width
|
||||||
visible: source.visible
|
visible: source.visible
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
FontAwesome {
|
CheckBox {
|
||||||
clip: true
|
|
||||||
id: check
|
id: check
|
||||||
verticalAlignment: Text.AlignVCenter
|
// FIXME: Should use radio buttons if source.exclusiveGroup.
|
||||||
horizontalAlignment: Text.AlignHCenter
|
anchors {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
left: parent.left
|
||||||
color: label.color
|
leftMargin: hifi.dimensions.menuPadding.x
|
||||||
text: checkText()
|
top: label.top
|
||||||
size: label.height
|
topMargin: 0
|
||||||
visible: source.visible
|
}
|
||||||
font.pixelSize: size
|
width: 20
|
||||||
function checkText() {
|
visible: source.visible && source.type === 1 && source.checkable
|
||||||
if (!source || source.type != 1 || !source.checkable) {
|
checked: setChecked()
|
||||||
return ""
|
function setChecked() {
|
||||||
|
if (!source || source.type !== 1 || !source.checkable) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// FIXME this works for native QML menus but I don't think it will
|
// FIXME this works for native QML menus but I don't think it will
|
||||||
// for proxied QML menus
|
// for proxied QML menus
|
||||||
if (source.exclusiveGroup) {
|
return source.checked;
|
||||||
return source.checked ? "\uF05D" : "\uF10C"
|
|
||||||
}
|
|
||||||
return source.checked ? "\uF046" : "\uF096"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
RalewaySemiBold {
|
||||||
id: label
|
id: label
|
||||||
|
size: hifi.fontSizes.rootMenu
|
||||||
|
font.capitalization: isSubMenu ? Font.MixedCase : Font.AllUppercase
|
||||||
anchors.left: check.right
|
anchors.left: check.right
|
||||||
anchors.leftMargin: 4
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
|
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50
|
||||||
enabled: source.visible && (source.type !== 0 ? source.enabled : false)
|
enabled: source.visible && (source.type !== 0 ? source.enabled : false)
|
||||||
visible: source.visible
|
visible: source.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
FontAwesome {
|
Item {
|
||||||
id: tag
|
id: separator
|
||||||
x: root.parent.width - width
|
anchors {
|
||||||
size: label.height
|
fill: parent
|
||||||
width: implicitWidth
|
leftMargin: hifi.dimensions.menuPadding.x + check.width
|
||||||
visible: source.visible && (source.type == 2)
|
rightMargin: hifi.dimensions.menuPadding.x + tail.width
|
||||||
text: "\uF0DA"
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
visible: source.type === MenuItemType.Separator
|
||||||
color: label.color
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
height: 1
|
||||||
|
color: hifi.colors.lightGray50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: tail
|
||||||
|
width: 48 + (shortcut.visible ? shortcut.width : 0)
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: hifi.dimensions.menuPadding.x
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewayLight {
|
||||||
|
id: shortcut
|
||||||
|
text: source.shortcut ? source.shortcut : ""
|
||||||
|
size: hifi.fontSizes.shortcutText
|
||||||
|
color: hifi.colors.baseGrayShadow
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 15
|
||||||
|
visible: source.visible && text != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
text: hifi.glyphs.disclosureExpand
|
||||||
|
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25
|
||||||
|
size: 2 * hifi.fontSizes.rootMenuDisclosure
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
visible: source.visible && (source.type === 2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,59 @@
|
||||||
import QtQuick 2.4
|
//
|
||||||
import QtQuick.Controls 1.3
|
// VrMenuView.qml
|
||||||
import QtQuick.Controls.Styles 1.3
|
//
|
||||||
|
// Created by Bradley Austin Davis on 18 Jan 2016
|
||||||
|
// Copyright 2016 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 "../styles"
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
import "../styles-uit"
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: root
|
id: root
|
||||||
implicitHeight: border.height
|
implicitHeight: background.height
|
||||||
implicitWidth: border.width
|
implicitWidth: background.width
|
||||||
|
|
||||||
property alias currentItem: listView.currentItem
|
property alias currentItem: listView.currentItem
|
||||||
property alias model: listView.model
|
property alias model: listView.model
|
||||||
|
property bool isSubMenu: false
|
||||||
signal selected(var item)
|
signal selected(var item)
|
||||||
|
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
Border {
|
Rectangle {
|
||||||
id: border
|
id: background
|
||||||
anchors.fill: listView
|
anchors.fill: listView
|
||||||
anchors.margins: -8
|
radius: hifi.dimensions.borderRadius
|
||||||
border.color: hifi.colors.hifiBlue
|
border.width: hifi.dimensions.borderWidth
|
||||||
color: hifi.colors.window
|
border.color: hifi.colors.lightGrayText80
|
||||||
// color: "#7f7f7f7f"
|
color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listView
|
id: listView
|
||||||
x: 8; y: 8
|
x: 8; y: 8
|
||||||
HifiConstants { id: hifi }
|
|
||||||
width: 128
|
width: 128
|
||||||
height: count * 32
|
height: count * 32
|
||||||
|
topMargin: hifi.dimensions.menuPadding.y
|
||||||
onEnabledChanged: recalcSize();
|
onEnabledChanged: recalcSize();
|
||||||
onVisibleChanged: recalcSize();
|
onVisibleChanged: recalcSize();
|
||||||
onCountChanged: recalcSize();
|
onCountChanged: recalcSize();
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
highlight: Rectangle {
|
highlight: Rectangle {
|
||||||
width: listView.currentItem ? listView.currentItem.width : 0
|
anchors {
|
||||||
height: listView.currentItem ? listView.currentItem.height : 0
|
left: parent ? parent.left : undefined
|
||||||
color: "lightsteelblue"; radius: 3
|
right: parent ? parent.right : undefined
|
||||||
|
leftMargin: hifi.dimensions.borderWidth
|
||||||
|
rightMargin: hifi.dimensions.borderWidth
|
||||||
|
}
|
||||||
|
color: hifi.colors.white
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: VrMenuItem {
|
delegate: VrMenuItem {
|
||||||
|
@ -75,6 +90,7 @@ FocusScope {
|
||||||
newHeight += currentItem.implicitHeight
|
newHeight += currentItem.implicitHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newHeight += 2 * hifi.dimensions.menuPadding.y; // White space at top and bottom.
|
||||||
if (maxWidth > width) {
|
if (maxWidth > width) {
|
||||||
width = maxWidth;
|
width = maxWidth;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
//
|
|
||||||
// FontAwesome.qml
|
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 24 Apr 2015
|
|
||||||
// Copyright 2015 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 QtQuick.Controls.Styles 1.4
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: root
|
|
||||||
FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; }
|
|
||||||
property int size: 32
|
|
||||||
width: size
|
|
||||||
height: size
|
|
||||||
font.pixelSize: size
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
font.family: iconFont.name
|
|
||||||
}
|
|
||||||
|
|
|
@ -67,9 +67,13 @@ Item {
|
||||||
readonly property color darkGray30: "#4d121212"
|
readonly property color darkGray30: "#4d121212"
|
||||||
readonly property color darkGray0: "#00121212"
|
readonly property color darkGray0: "#00121212"
|
||||||
readonly property color baseGrayShadow60: "#99252525"
|
readonly property color baseGrayShadow60: "#99252525"
|
||||||
|
readonly property color baseGrayShadow50: "#80252525"
|
||||||
|
readonly property color baseGrayShadow25: "#40252525"
|
||||||
readonly property color baseGrayHighlight40: "#66575757"
|
readonly property color baseGrayHighlight40: "#66575757"
|
||||||
readonly property color baseGrayHighlight15: "#26575757"
|
readonly property color baseGrayHighlight15: "#26575757"
|
||||||
|
readonly property color lightGray50: "#806a6a6a"
|
||||||
readonly property color lightGrayText80: "#ccafafaf"
|
readonly property color lightGrayText80: "#ccafafaf"
|
||||||
|
readonly property color faintGray80: "#cce3e3e3"
|
||||||
readonly property color faintGray50: "#80e3e3e3"
|
readonly property color faintGray50: "#80e3e3e3"
|
||||||
|
|
||||||
// Other colors
|
// Other colors
|
||||||
|
@ -135,6 +139,7 @@ Item {
|
||||||
readonly property real modalDialogTitleHeight: 40
|
readonly property real modalDialogTitleHeight: 40
|
||||||
readonly property real controlLineHeight: 29 // Height of spinbox control on 1920 x 1080 monitor
|
readonly property real controlLineHeight: 29 // Height of spinbox control on 1920 x 1080 monitor
|
||||||
readonly property real controlInterlineHeight: 22 // 75% of controlLineHeight
|
readonly property real controlInterlineHeight: 22 // 75% of controlLineHeight
|
||||||
|
readonly property vector2d menuPadding: Qt.vector2d(14, 12)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -152,6 +157,7 @@ Item {
|
||||||
readonly property real logs: dimensions.largeScreen ? 16 : 12
|
readonly property real logs: dimensions.largeScreen ? 16 : 12
|
||||||
readonly property real code: dimensions.largeScreen ? 16 : 12
|
readonly property real code: dimensions.largeScreen ? 16 : 12
|
||||||
readonly property real rootMenu: dimensions.largeScreen ? 15 : 11
|
readonly property real rootMenu: dimensions.largeScreen ? 15 : 11
|
||||||
|
readonly property real rootMenuDisclosure: dimensions.largeScreen ? 20 : 16
|
||||||
readonly property real menuItem: dimensions.largeScreen ? 15 : 11
|
readonly property real menuItem: dimensions.largeScreen ? 15 : 11
|
||||||
readonly property real shortcutText: dimensions.largeScreen ? 13 : 9
|
readonly property real shortcutText: dimensions.largeScreen ? 13 : 9
|
||||||
readonly property real carat: dimensions.largeScreen ? 38 : 30
|
readonly property real carat: dimensions.largeScreen ? 38 : 30
|
||||||
|
|
|
@ -3179,7 +3179,6 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
auto myAvatar = getMyAvatar();
|
auto myAvatar = getMyAvatar();
|
||||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||||
userInputMapper->update(deltaTime);
|
|
||||||
|
|
||||||
controller::InputCalibrationData calibrationData = {
|
controller::InputCalibrationData calibrationData = {
|
||||||
myAvatar->getSensorToWorldMatrix(),
|
myAvatar->getSensorToWorldMatrix(),
|
||||||
|
@ -3187,9 +3186,12 @@ void Application::update(float deltaTime) {
|
||||||
myAvatar->getHMDSensorMatrix()
|
myAvatar->getHMDSensorMatrix()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
InputPluginPointer keyboardMousePlugin;
|
||||||
bool jointsCaptured = false;
|
bool jointsCaptured = false;
|
||||||
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
|
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
|
||||||
if (inputPlugin->isActive()) {
|
if (inputPlugin->getName() == KeyboardMouseDevice::NAME) {
|
||||||
|
keyboardMousePlugin = inputPlugin;
|
||||||
|
} else if (inputPlugin->isActive()) {
|
||||||
inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
|
inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
|
||||||
if (inputPlugin->isJointController()) {
|
if (inputPlugin->isJointController()) {
|
||||||
jointsCaptured = true;
|
jointsCaptured = true;
|
||||||
|
@ -3197,6 +3199,12 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userInputMapper->update(deltaTime);
|
||||||
|
|
||||||
|
if (keyboardMousePlugin && keyboardMousePlugin->isActive()) {
|
||||||
|
keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
|
||||||
|
}
|
||||||
|
|
||||||
_controllerScriptingInterface->updateInputControllers();
|
_controllerScriptingInterface->updateInputControllers();
|
||||||
|
|
||||||
// Transfer the user inputs to the driveKeys
|
// Transfer the user inputs to the driveKeys
|
||||||
|
@ -3216,16 +3224,14 @@ void Application::update(float deltaTime) {
|
||||||
myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
|
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
|
||||||
controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
|
controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
|
||||||
Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
|
auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
|
||||||
setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));
|
myAvatar->setHandControllerPosesInWorldFrame(leftHandPose.transform(myAvatarMatrix), rightHandPose.transform(myAvatarMatrix));
|
||||||
setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));
|
|
||||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||||
|
|
||||||
_avatarUpdate->synchronousProcess();
|
|
||||||
|
|
||||||
if (_physicsEnabled) {
|
if (_physicsEnabled) {
|
||||||
PerformanceTimer perfTimer("physics");
|
PerformanceTimer perfTimer("physics");
|
||||||
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
|
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
|
||||||
|
@ -3297,6 +3303,8 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_avatarUpdate->synchronousProcess();
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("overlays");
|
PerformanceTimer perfTimer("overlays");
|
||||||
_overlays.update(deltaTime);
|
_overlays.update(deltaTime);
|
||||||
|
@ -4983,49 +4991,6 @@ mat4 Application::getHMDSensorPose() const {
|
||||||
return mat4();
|
return mat4();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) {
|
|
||||||
|
|
||||||
// NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't
|
|
||||||
// reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more
|
|
||||||
// of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about
|
|
||||||
// controller::Pose. More work is needed to clean this up.
|
|
||||||
hand->modifyPalm(whichHand, [&](PalmData& palm) {
|
|
||||||
palm.setActive(pose.isValid());
|
|
||||||
|
|
||||||
// controller pose is in Avatar frame.
|
|
||||||
glm::vec3 position = pose.getTranslation();
|
|
||||||
glm::quat rotation = pose.getRotation();
|
|
||||||
glm::vec3 rawVelocity = pose.getVelocity();
|
|
||||||
glm::vec3 angularVelocity = pose.getAngularVelocity();
|
|
||||||
|
|
||||||
palm.setRawVelocity(rawVelocity);
|
|
||||||
palm.setRawAngularVelocity(angularVelocity);
|
|
||||||
|
|
||||||
if (controller::InputDevice::getLowVelocityFilter()) {
|
|
||||||
// Use a velocity sensitive filter to damp small motions and preserve large ones with
|
|
||||||
// no latency.
|
|
||||||
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
|
|
||||||
position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
|
|
||||||
rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter);
|
|
||||||
}
|
|
||||||
palm.setRawPosition(position);
|
|
||||||
palm.setRawRotation(rotation);
|
|
||||||
|
|
||||||
// Store the one fingertip in the palm structure so we can track velocity
|
|
||||||
const float FINGER_LENGTH = 0.3f; // meters
|
|
||||||
const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
|
|
||||||
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
|
|
||||||
glm::vec3 oldTipPosition = palm.getTipRawPosition();
|
|
||||||
if (deltaTime > 0.0f) {
|
|
||||||
palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
|
|
||||||
} else {
|
|
||||||
palm.setTipVelocity(glm::vec3(0.0f));
|
|
||||||
}
|
|
||||||
palm.setTipPosition(newTipPosition);
|
|
||||||
palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::crashApplication() {
|
void Application::crashApplication() {
|
||||||
qCDebug(interfaceapp) << "Intentionally crashed Interface";
|
qCDebug(interfaceapp) << "Intentionally crashed Interface";
|
||||||
QObject* object = nullptr;
|
QObject* object = nullptr;
|
||||||
|
|
|
@ -299,7 +299,7 @@ private slots:
|
||||||
|
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
|
|
||||||
bool acceptSnapshot(const QString& urlString);
|
bool acceptSnapshot(const QString& urlString);
|
||||||
bool askToSetAvatarUrl(const QString& url);
|
bool askToSetAvatarUrl(const QString& url);
|
||||||
bool askToLoadScript(const QString& scriptFilenameOrURL);
|
bool askToLoadScript(const QString& scriptFilenameOrURL);
|
||||||
|
@ -327,8 +327,6 @@ private:
|
||||||
|
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
|
|
||||||
void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
|
|
||||||
|
|
||||||
// Various helper functions called during update()
|
// Various helper functions called during update()
|
||||||
void updateLOD();
|
void updateLOD();
|
||||||
void updateThreads(float deltaTime);
|
void updateThreads(float deltaTime);
|
||||||
|
|
|
@ -481,7 +481,8 @@ Menu::Menu() {
|
||||||
|
|
||||||
// Developer > Hands >>>
|
// Developer > Hands >>>
|
||||||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false,
|
||||||
|
avatar, SLOT(setEnableDebugDrawHandControllers(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
|
||||||
qApp, SLOT(setLowVelocityFilter(bool)));
|
qApp, SLOT(setLowVelocityFilter(bool)));
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "AvatarManager.h"
|
#include "AvatarManager.h"
|
||||||
#include "AvatarMotionState.h"
|
#include "AvatarMotionState.h"
|
||||||
#include "Hand.h"
|
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "Physics.h"
|
#include "Physics.h"
|
||||||
|
@ -101,7 +100,6 @@ Avatar::Avatar(RigPointer rig) :
|
||||||
|
|
||||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||||
_headData = static_cast<HeadData*>(new Head(this));
|
_headData = static_cast<HeadData*>(new Head(this));
|
||||||
_handData = static_cast<HandData*>(new Hand(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar::~Avatar() {
|
Avatar::~Avatar() {
|
||||||
|
@ -190,11 +188,6 @@ void Avatar::simulate(float deltaTime) {
|
||||||
float boundingRadius = getBoundingRadius();
|
float boundingRadius = getBoundingRadius();
|
||||||
bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
|
bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
|
||||||
|
|
||||||
{
|
|
||||||
PerformanceTimer perfTimer("hand");
|
|
||||||
getHand()->simulate(deltaTime, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_shouldAnimate && !_shouldSkipRender && inView) {
|
if (_shouldAnimate && !_shouldSkipRender && inView) {
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("skeleton");
|
PerformanceTimer perfTimer("skeleton");
|
||||||
|
@ -578,11 +571,6 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, floa
|
||||||
if (_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable()) {
|
if (_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable()) {
|
||||||
getHead()->render(renderArgs, 1.0f, renderFrustum);
|
getHead()->render(renderArgs, 1.0f, renderFrustum);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
|
|
||||||
Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) {
|
|
||||||
getHand()->renderHandTargets(renderArgs, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
getHead()->renderLookAts(renderArgs);
|
getHead()->renderLookAts(renderArgs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
|
|
||||||
#include <render/Scene.h>
|
#include <render/Scene.h>
|
||||||
|
|
||||||
#include "Hand.h"
|
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
#include "SkeletonModel.h"
|
#include "SkeletonModel.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
|
@ -91,7 +90,7 @@ public:
|
||||||
float getUniformScale() const { return getScale().y; }
|
float getUniformScale() const { return getScale().y; }
|
||||||
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
||||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||||
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
|
||||||
glm::quat getWorldAlignedOrientation() const;
|
glm::quat getWorldAlignedOrientation() const;
|
||||||
|
|
||||||
AABox getBounds() const;
|
AABox getBounds() const;
|
||||||
|
|
|
@ -104,48 +104,57 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(float deltaTimeStep, glm::qu
|
||||||
|
|
||||||
withReadLock([&]{
|
withReadLock([&]{
|
||||||
bool isRightHand = (_hand == "right");
|
bool isRightHand = (_hand == "right");
|
||||||
|
|
||||||
glm::vec3 palmPosition;
|
glm::vec3 palmPosition;
|
||||||
glm::quat palmRotation;
|
glm::quat palmRotation;
|
||||||
|
|
||||||
PalmData palmData = holdingAvatar->getHand()->getCopyOfPalmData(isRightHand ? HandData::RightHand : HandData::LeftHand);
|
if (holdingAvatar->isMyAvatar()) {
|
||||||
|
|
||||||
if (palmData.isValid()) {
|
|
||||||
// TODO: adjust according to _relativePosition and _relativeRotation?
|
|
||||||
linearVelocity = palmData.getVelocity();
|
|
||||||
angularVelocity = palmData.getAngularVelocity();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ignoreIK && holdingAvatar->isMyAvatar() && palmData.isValid()) {
|
|
||||||
// We cannot ignore other avatars IK and this is not the point of this option
|
|
||||||
// This is meant to make the grabbing behavior more reactive.
|
|
||||||
palmPosition = palmData.getPosition();
|
|
||||||
palmRotation = palmData.getRotation();
|
|
||||||
} else if (holdingAvatar->isMyAvatar()) {
|
|
||||||
glm::vec3 avatarRigidBodyPosition;
|
|
||||||
glm::quat avatarRigidBodyRotation;
|
|
||||||
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
|
||||||
|
|
||||||
// the offset and rotation between the avatar's rigid body and the palm were determined earlier
|
|
||||||
// in prepareForPhysicsSimulation. At this point, the avatar's rigid body has been moved by bullet
|
|
||||||
// and the data in the Avatar class is stale. This means that the result of get*PalmPosition will
|
|
||||||
// be stale. Instead, determine the current palm position with the current avatar's rigid body
|
|
||||||
// location and the saved offsets.
|
|
||||||
|
|
||||||
// this line is more correct but breaks for the current way avatar data is updated.
|
|
||||||
// palmPosition = avatarRigidBodyPosition + avatarRigidBodyRotation * _palmOffsetFromRigidBody;
|
|
||||||
// instead, use this for now:
|
|
||||||
palmPosition = avatarRigidBodyPosition + _palmOffsetFromRigidBody;
|
|
||||||
|
|
||||||
// the item jitters the least by getting the rotation based on the opinion of Avatar.h rather
|
|
||||||
// than that of the rigid body. leaving this next line here for future reference:
|
|
||||||
// palmRotation = avatarRigidBodyRotation * _palmRotationFromRigidBody;
|
|
||||||
|
|
||||||
|
// fetch the hand controller pose
|
||||||
|
controller::Pose pose;
|
||||||
if (isRightHand) {
|
if (isRightHand) {
|
||||||
palmRotation = holdingAvatar->getRightPalmRotation();
|
pose = avatarManager->getMyAvatar()->getRightHandControllerPoseInWorldFrame();
|
||||||
} else {
|
} else {
|
||||||
palmRotation = holdingAvatar->getLeftPalmRotation();
|
pose = avatarManager->getMyAvatar()->getLeftHandControllerPoseInWorldFrame();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
if (pose.isValid()) {
|
||||||
|
linearVelocity = pose.getVelocity();
|
||||||
|
angularVelocity = pose.getAngularVelocity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ignoreIK && pose.isValid()) {
|
||||||
|
// We cannot ignore other avatars IK and this is not the point of this option
|
||||||
|
// This is meant to make the grabbing behavior more reactive.
|
||||||
|
palmPosition = pose.getTranslation();
|
||||||
|
palmRotation = pose.getRotation();
|
||||||
|
} else {
|
||||||
|
glm::vec3 avatarRigidBodyPosition;
|
||||||
|
glm::quat avatarRigidBodyRotation;
|
||||||
|
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
||||||
|
|
||||||
|
// the offset and rotation between the avatar's rigid body and the palm were determined earlier
|
||||||
|
// in prepareForPhysicsSimulation. At this point, the avatar's rigid body has been moved by bullet
|
||||||
|
// and the data in the Avatar class is stale. This means that the result of get*PalmPosition will
|
||||||
|
// be stale. Instead, determine the current palm position with the current avatar's rigid body
|
||||||
|
// location and the saved offsets.
|
||||||
|
|
||||||
|
// this line is more correct but breaks for the current way avatar data is updated.
|
||||||
|
// palmPosition = avatarRigidBodyPosition + avatarRigidBodyRotation * _palmOffsetFromRigidBody;
|
||||||
|
// instead, use this for now:
|
||||||
|
palmPosition = avatarRigidBodyPosition + _palmOffsetFromRigidBody;
|
||||||
|
|
||||||
|
// the item jitters the least by getting the rotation based on the opinion of Avatar.h rather
|
||||||
|
// than that of the rigid body. leaving this next line here for future reference:
|
||||||
|
// palmRotation = avatarRigidBodyRotation * _palmRotationFromRigidBody;
|
||||||
|
|
||||||
|
if (isRightHand) {
|
||||||
|
palmRotation = holdingAvatar->getRightPalmRotation();
|
||||||
|
} else {
|
||||||
|
palmRotation = holdingAvatar->getLeftPalmRotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // regular avatar
|
||||||
if (isRightHand) {
|
if (isRightHand) {
|
||||||
palmPosition = holdingAvatar->getRightPalmPosition();
|
palmPosition = holdingAvatar->getRightPalmPosition();
|
||||||
palmRotation = holdingAvatar->getRightPalmRotation();
|
palmRotation = holdingAvatar->getRightPalmRotation();
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
//
|
|
||||||
// Hand.cpp
|
|
||||||
// interface/src/avatar
|
|
||||||
//
|
|
||||||
// Copyright 2013 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 "Hand.h"
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#include <GeometryUtil.h>
|
|
||||||
#include <RenderArgs.h>
|
|
||||||
|
|
||||||
#include "Avatar.h"
|
|
||||||
#include "AvatarManager.h"
|
|
||||||
#include "MyAvatar.h"
|
|
||||||
#include "Util.h"
|
|
||||||
#include "world.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
Hand::Hand(Avatar* owningAvatar) :
|
|
||||||
HandData((AvatarData*)owningAvatar),
|
|
||||||
_owningAvatar(owningAvatar)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Hand::simulate(float deltaTime, bool isMine) {
|
|
||||||
// nothing to do here
|
|
||||||
}
|
|
||||||
|
|
||||||
void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
|
|
||||||
float avatarScale = 1.0f;
|
|
||||||
if (_owningAvatar) {
|
|
||||||
avatarScale = _owningAvatar->getUniformScale();
|
|
||||||
}
|
|
||||||
|
|
||||||
const float alpha = 1.0f;
|
|
||||||
const glm::vec3 redColor(1.0f, 0.0f, 0.0f); // Color the hand targets red to be different than skin
|
|
||||||
const glm::vec3 greenColor(0.0f, 1.0f, 0.0f); // Color the hand targets red to be different than skin
|
|
||||||
const glm::vec3 blueColor(0.0f, 0.0f, 1.0f); // Color the hand targets red to be different than skin
|
|
||||||
const glm::vec3 grayColor(0.5f);
|
|
||||||
const float SPHERE_RADIUS = 0.03f * avatarScale;
|
|
||||||
|
|
||||||
auto palms = getCopyOfPalms();
|
|
||||||
|
|
||||||
gpu::Batch& batch = *renderArgs->_batch;
|
|
||||||
if (isMine) {
|
|
||||||
for (const auto& palm : palms) {
|
|
||||||
if (!palm.isActive()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// draw a gray sphere at the target position of the "Hand" joint
|
|
||||||
glm::vec3 position = palm.getPosition();
|
|
||||||
Transform transform = Transform();
|
|
||||||
transform.setTranslation(position);
|
|
||||||
transform.setRotation(palm.getRotation());
|
|
||||||
transform.postScale(SPHERE_RADIUS);
|
|
||||||
batch.setModelTransform(transform);
|
|
||||||
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, grayColor);
|
|
||||||
|
|
||||||
// draw a green sphere at the old "finger tip"
|
|
||||||
transform = Transform();
|
|
||||||
position = palm.getTipPosition();
|
|
||||||
transform.setTranslation(position);
|
|
||||||
transform.setRotation(palm.getRotation());
|
|
||||||
transform.postScale(SPHERE_RADIUS);
|
|
||||||
batch.setModelTransform(transform);
|
|
||||||
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, greenColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const float AXIS_RADIUS = 0.1f * SPHERE_RADIUS;
|
|
||||||
const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;
|
|
||||||
|
|
||||||
// Draw the coordinate frames of the hand targets
|
|
||||||
for (const auto& palm : palms) {
|
|
||||||
if (palm.isActive()) {
|
|
||||||
glm::vec3 root = palm.getPosition();
|
|
||||||
|
|
||||||
const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
|
||||||
glm::quat palmRotation = palm.getRotation();
|
|
||||||
Transform transform = Transform();
|
|
||||||
transform.setTranslation(glm::vec3());
|
|
||||||
batch.setModelTransform(transform);
|
|
||||||
glm::vec3 tip = root + palmRotation * glm::vec3(AXIS_LENGTH, 0.0f, 0.0f);
|
|
||||||
Avatar::renderJointConnectingCone(batch, root, tip, AXIS_RADIUS, AXIS_RADIUS, glm::vec4(redColor.r, redColor.g, redColor.b, alpha));
|
|
||||||
|
|
||||||
tip = root + palmRotation * glm::vec3(0.0f, AXIS_LENGTH, 0.0f);
|
|
||||||
Avatar::renderJointConnectingCone(batch, root, tip, AXIS_RADIUS, AXIS_RADIUS, glm::vec4(greenColor.r, greenColor.g, greenColor.b, alpha));
|
|
||||||
|
|
||||||
tip = root + palmRotation * glm::vec3(0.0f, 0.0f, AXIS_LENGTH);
|
|
||||||
Avatar::renderJointConnectingCone(batch, root, tip, AXIS_RADIUS, AXIS_RADIUS, glm::vec4(blueColor.r, blueColor.g, blueColor.b, alpha));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
//
|
|
||||||
// Hand.h
|
|
||||||
// interface/src/avatar
|
|
||||||
//
|
|
||||||
// Copyright 2013 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_Hand_h
|
|
||||||
#define hifi_Hand_h
|
|
||||||
|
|
||||||
#include <HandData.h>
|
|
||||||
|
|
||||||
class Avatar;
|
|
||||||
class RenderArgs;
|
|
||||||
|
|
||||||
class Hand : public HandData {
|
|
||||||
public:
|
|
||||||
Hand(Avatar* owningAvatar);
|
|
||||||
|
|
||||||
void simulate(float deltaTime, bool isMine);
|
|
||||||
void renderHandTargets(RenderArgs* renderArgs, bool isMine);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// disallow copies of the Hand, copy of owning Avatar is disallowed too
|
|
||||||
Hand(const Hand&);
|
|
||||||
Hand& operator= (const Hand&);
|
|
||||||
|
|
||||||
int _controllerButtons; /// Button states read from hand-held controllers
|
|
||||||
|
|
||||||
Avatar* _owningAvatar;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_Hand_h
|
|
|
@ -341,12 +341,6 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
updatePosition(deltaTime);
|
updatePosition(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
PerformanceTimer perfTimer("hand");
|
|
||||||
// update avatar skeleton and simulate hand and head
|
|
||||||
getHand()->simulate(deltaTime, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("skeleton");
|
PerformanceTimer perfTimer("skeleton");
|
||||||
_skeletonModel.simulate(deltaTime);
|
_skeletonModel.simulate(deltaTime);
|
||||||
|
@ -522,49 +516,50 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
||||||
-MAX_LEAN, MAX_LEAN));
|
-MAX_LEAN, MAX_LEAN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
glm::vec3 MyAvatar::getLeftHandPosition() const {
|
glm::vec3 MyAvatar::getLeftHandPosition() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
auto pose = getLeftHandControllerPoseInAvatarFrame();
|
||||||
return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
|
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 MyAvatar::getRightHandPosition() const {
|
glm::vec3 MyAvatar::getRightHandPosition() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
auto pose = getRightHandControllerPoseInAvatarFrame();
|
||||||
return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
|
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 MyAvatar::getLeftHandTipPosition() const {
|
glm::vec3 MyAvatar::getLeftHandTipPosition() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
const float TIP_LENGTH = 0.3f;
|
||||||
return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
|
auto pose = getLeftHandControllerPoseInAvatarFrame();
|
||||||
|
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 MyAvatar::getRightHandTipPosition() const {
|
glm::vec3 MyAvatar::getRightHandTipPosition() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
const float TIP_LENGTH = 0.3f;
|
||||||
return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
|
auto pose = getRightHandControllerPoseInAvatarFrame();
|
||||||
|
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getLeftHandPose() const {
|
controller::Pose MyAvatar::getLeftHandPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
return getLeftHandControllerPoseInAvatarFrame();
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
|
||||||
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getRightHandPose() const {
|
controller::Pose MyAvatar::getRightHandPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
return getRightHandControllerPoseInAvatarFrame();
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
|
||||||
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getLeftHandTipPose() const {
|
controller::Pose MyAvatar::getLeftHandTipPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
auto pose = getLeftHandControllerPoseInAvatarFrame();
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
glm::vec3 tipTrans = getLeftHandTipPosition();
|
||||||
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
|
||||||
|
pose.translation = tipTrans;
|
||||||
|
return pose;
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getRightHandTipPose() const {
|
controller::Pose MyAvatar::getRightHandTipPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
auto pose = getRightHandControllerPoseInAvatarFrame();
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
glm::vec3 tipTrans = getRightHandTipPosition();
|
||||||
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
|
||||||
|
pose.translation = tipTrans;
|
||||||
|
return pose;
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
|
@ -702,6 +697,15 @@ void MyAvatar::setEnableDebugDrawPosition(bool isEnabled) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setEnableDebugDrawHandControllers(bool isEnabled) {
|
||||||
|
_enableDebugDrawHandControllers = isEnabled;
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
DebugDraw::getInstance().removeMarker("leftHandController");
|
||||||
|
DebugDraw::getInstance().removeMarker("rightHandController");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) {
|
void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) {
|
||||||
_enableDebugDrawSensorToWorldMatrix = isEnabled;
|
_enableDebugDrawSensorToWorldMatrix = isEnabled;
|
||||||
|
|
||||||
|
@ -1092,6 +1096,48 @@ void MyAvatar::rebuildCollisionShape() {
|
||||||
_characterController.setLocalBoundingBox(corner, diagonal);
|
_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::setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||||
|
if (controller::InputDevice::getLowVelocityFilter()) {
|
||||||
|
auto oldLeftPose = getLeftHandControllerPoseInWorldFrame();
|
||||||
|
auto oldRightPose = getRightHandControllerPoseInWorldFrame();
|
||||||
|
_leftHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
|
||||||
|
_rightHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
|
||||||
|
} else {
|
||||||
|
_leftHandControllerPoseInWorldFrameCache.set(left);
|
||||||
|
_rightHandControllerPoseInWorldFrameCache.set(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const {
|
||||||
|
return _leftHandControllerPoseInWorldFrameCache.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const {
|
||||||
|
return _rightHandControllerPoseInWorldFrameCache.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const {
|
||||||
|
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||||
|
return getLeftHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
|
||||||
|
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
|
||||||
|
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::prepareForPhysicsSimulation() {
|
void MyAvatar::prepareForPhysicsSimulation() {
|
||||||
relayDriveKeysToCharacterController();
|
relayDriveKeysToCharacterController();
|
||||||
|
|
||||||
|
@ -1227,11 +1273,6 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
|
||||||
} else {
|
} else {
|
||||||
getHead()->renderLookAts(renderArgs);
|
getHead()->renderLookAts(renderArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
|
|
||||||
Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) {
|
|
||||||
getHand()->renderHandTargets(renderArgs, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
|
void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
|
||||||
|
@ -1340,6 +1381,23 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_enableDebugDrawHandControllers) {
|
||||||
|
auto leftHandPose = getLeftHandControllerPoseInWorldFrame();
|
||||||
|
auto rightHandPose = getRightHandControllerPoseInWorldFrame();
|
||||||
|
|
||||||
|
if (leftHandPose.isValid()) {
|
||||||
|
DebugDraw::getInstance().addMarker("leftHandController", leftHandPose.getRotation(), leftHandPose.getTranslation(), glm::vec4(1));
|
||||||
|
} else {
|
||||||
|
DebugDraw::getInstance().removeMarker("leftHandController");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightHandPose.isValid()) {
|
||||||
|
DebugDraw::getInstance().addMarker("rightHandController", rightHandPose.getRotation(), rightHandPose.getTranslation(), glm::vec4(1));
|
||||||
|
} else {
|
||||||
|
DebugDraw::getInstance().removeMarker("rightHandController");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DebugDraw::getInstance().updateMyAvatarPos(getPosition());
|
DebugDraw::getInstance().updateMyAvatarPos(getPosition());
|
||||||
DebugDraw::getInstance().updateMyAvatarRot(getOrientation());
|
DebugDraw::getInstance().updateMyAvatarRot(getOrientation());
|
||||||
|
|
||||||
|
|
|
@ -249,6 +249,12 @@ public:
|
||||||
|
|
||||||
virtual void rebuildCollisionShape() override;
|
virtual void rebuildCollisionShape() override;
|
||||||
|
|
||||||
|
void setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right);
|
||||||
|
controller::Pose getLeftHandControllerPoseInWorldFrame() const;
|
||||||
|
controller::Pose getRightHandControllerPoseInWorldFrame() const;
|
||||||
|
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
|
||||||
|
controller::Pose getRightHandControllerPoseInAvatarFrame() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
void decreaseSize();
|
void decreaseSize();
|
||||||
|
@ -271,6 +277,7 @@ public slots:
|
||||||
void setEnableDebugDrawDefaultPose(bool isEnabled);
|
void setEnableDebugDrawDefaultPose(bool isEnabled);
|
||||||
void setEnableDebugDrawAnimPose(bool isEnabled);
|
void setEnableDebugDrawAnimPose(bool isEnabled);
|
||||||
void setEnableDebugDrawPosition(bool isEnabled);
|
void setEnableDebugDrawPosition(bool isEnabled);
|
||||||
|
void setEnableDebugDrawHandControllers(bool isEnabled);
|
||||||
void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled);
|
void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled);
|
||||||
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
||||||
void setEnableMeshVisible(bool isEnabled);
|
void setEnableMeshVisible(bool isEnabled);
|
||||||
|
@ -435,6 +442,7 @@ private:
|
||||||
|
|
||||||
bool _enableDebugDrawDefaultPose { false };
|
bool _enableDebugDrawDefaultPose { false };
|
||||||
bool _enableDebugDrawAnimPose { false };
|
bool _enableDebugDrawAnimPose { false };
|
||||||
|
bool _enableDebugDrawHandControllers { false };
|
||||||
bool _enableDebugDrawSensorToWorldMatrix { false };
|
bool _enableDebugDrawSensorToWorldMatrix { false };
|
||||||
|
|
||||||
AudioListenerMode _audioListenerMode;
|
AudioListenerMode _audioListenerMode;
|
||||||
|
@ -446,6 +454,10 @@ private:
|
||||||
bool _hoverReferenceCameraFacingIsCaptured { false };
|
bool _hoverReferenceCameraFacingIsCaptured { false };
|
||||||
glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space
|
glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space
|
||||||
|
|
||||||
|
// These are stored in WORLD frame
|
||||||
|
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInWorldFrameCache { controller::Pose() };
|
||||||
|
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInWorldFrameCache { controller::Pose() };
|
||||||
|
|
||||||
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
|
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
|
||||||
float AUDIO_ENERGY_CONSTANT { 0.000001f };
|
float AUDIO_ENERGY_CONSTANT { 0.000001f };
|
||||||
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };
|
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "Hand.h"
|
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "SkeletonModel.h"
|
#include "SkeletonModel.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
@ -127,20 +126,20 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
|
|
||||||
Rig::HandParameters handParams;
|
Rig::HandParameters handParams;
|
||||||
|
|
||||||
auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand);
|
auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
|
||||||
if (leftPalm.isValid() && leftPalm.isActive()) {
|
if (leftPose.isValid()) {
|
||||||
handParams.isLeftEnabled = true;
|
handParams.isLeftEnabled = true;
|
||||||
handParams.leftPosition = Quaternions::Y_180 * leftPalm.getRawPosition();
|
handParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation();
|
||||||
handParams.leftOrientation = Quaternions::Y_180 * leftPalm.getRawRotation();
|
handParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation();
|
||||||
} else {
|
} else {
|
||||||
handParams.isLeftEnabled = false;
|
handParams.isLeftEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand);
|
auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame();
|
||||||
if (rightPalm.isValid() && rightPalm.isActive()) {
|
if (rightPose.isValid()) {
|
||||||
handParams.isRightEnabled = true;
|
handParams.isRightEnabled = true;
|
||||||
handParams.rightPosition = Quaternions::Y_180 * rightPalm.getRawPosition();
|
handParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation();
|
||||||
handParams.rightOrientation = Quaternions::Y_180 * rightPalm.getRawRotation();
|
handParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation();
|
||||||
} else {
|
} else {
|
||||||
handParams.isRightEnabled = false;
|
handParams.isRightEnabled = false;
|
||||||
}
|
}
|
||||||
|
@ -247,17 +246,6 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
|
||||||
return firstIndex.value < secondIndex.value;
|
return firstIndex.value < secondIndex.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) {
|
|
||||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
|
||||||
int parentJointIndex = geometry.joints.at(jointIndex).parentIndex;
|
|
||||||
if (parentJointIndex == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const {
|
bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const {
|
||||||
int knuckleIndex = _rig->indexOfJoint("LeftHandMiddle1");
|
int knuckleIndex = _rig->indexOfJoint("LeftHandMiddle1");
|
||||||
int handIndex = _rig->indexOfJoint("LeftHand");
|
int handIndex = _rig->indexOfJoint("LeftHand");
|
||||||
|
|
|
@ -111,7 +111,6 @@ protected:
|
||||||
|
|
||||||
void computeBoundingShape();
|
void computeBoundingShape();
|
||||||
|
|
||||||
void applyPalmData(int jointIndex, const PalmData& palm);
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <avatar/AvatarManager.h>
|
#include <avatar/AvatarManager.h>
|
||||||
#include <avatar/MyAvatar.h>
|
#include <avatar/MyAvatar.h>
|
||||||
#include <HandData.h>
|
|
||||||
#include <HFBackEvent.h>
|
#include <HFBackEvent.h>
|
||||||
#include <plugins/PluginManager.h>
|
#include <plugins/PluginManager.h>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@ AvatarData::AvatarData() :
|
||||||
_hasNewJointRotations(true),
|
_hasNewJointRotations(true),
|
||||||
_hasNewJointTranslations(true),
|
_hasNewJointTranslations(true),
|
||||||
_headData(NULL),
|
_headData(NULL),
|
||||||
_handData(NULL),
|
|
||||||
_faceModelURL("http://invalid.com"),
|
_faceModelURL("http://invalid.com"),
|
||||||
_displayNameTargetAlpha(1.0f),
|
_displayNameTargetAlpha(1.0f),
|
||||||
_displayNameAlpha(1.0f),
|
_displayNameAlpha(1.0f),
|
||||||
|
@ -74,7 +73,6 @@ AvatarData::AvatarData() :
|
||||||
|
|
||||||
AvatarData::~AvatarData() {
|
AvatarData::~AvatarData() {
|
||||||
delete _headData;
|
delete _headData;
|
||||||
delete _handData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot have a file-level variable (const or otherwise) in the header if it uses PathUtils, because that references Application, which will not yet initialized.
|
// We cannot have a file-level variable (const or otherwise) in the header if it uses PathUtils, because that references Application, which will not yet initialized.
|
||||||
|
@ -418,11 +416,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
_headData = new HeadData(this);
|
_headData = new HeadData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// lazily allocate memory for HandData in case we're not an Avatar instance
|
|
||||||
if (!_handData) {
|
|
||||||
_handData = new HandData(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(buffer.data());
|
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(buffer.data());
|
||||||
const unsigned char* sourceBuffer = startPosition;
|
const unsigned char* sourceBuffer = startPosition;
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
|
|
|
@ -52,9 +52,9 @@ typedef unsigned long long quint64;
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
#include <SpatiallyNestable.h>
|
#include <SpatiallyNestable.h>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
#include "AABox.h"
|
#include "AABox.h"
|
||||||
#include "HandData.h"
|
|
||||||
#include "HeadData.h"
|
#include "HeadData.h"
|
||||||
#include "PathUtils.h"
|
#include "PathUtils.h"
|
||||||
|
|
||||||
|
@ -290,7 +290,6 @@ public:
|
||||||
KeyState keyState() const { return _keyState; }
|
KeyState keyState() const { return _keyState; }
|
||||||
|
|
||||||
const HeadData* getHeadData() const { return _headData; }
|
const HeadData* getHeadData() const { return _headData; }
|
||||||
const HandData* getHandData() const { return _handData; }
|
|
||||||
|
|
||||||
bool hasIdentityChangedAfterParsing(const QByteArray& data);
|
bool hasIdentityChangedAfterParsing(const QByteArray& data);
|
||||||
QByteArray identityByteArray();
|
QByteArray identityByteArray();
|
||||||
|
@ -383,7 +382,6 @@ protected:
|
||||||
bool _hasNewJointTranslations; // set in AvatarData, cleared in Avatar
|
bool _hasNewJointTranslations; // set in AvatarData, cleared in Avatar
|
||||||
|
|
||||||
HeadData* _headData;
|
HeadData* _headData;
|
||||||
HandData* _handData;
|
|
||||||
|
|
||||||
QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
|
QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
|
||||||
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
|
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
//
|
|
||||||
// HandData.cpp
|
|
||||||
// libraries/avatars/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 5/20/13.
|
|
||||||
// Copyright 2013 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 <QtCore/QDataStream>
|
|
||||||
|
|
||||||
#include <GeometryUtil.h>
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
|
|
||||||
#include "AvatarData.h"
|
|
||||||
#include "HandData.h"
|
|
||||||
|
|
||||||
|
|
||||||
HandData::HandData(AvatarData* owningAvatar) :
|
|
||||||
_owningAvatarData(owningAvatar)
|
|
||||||
{
|
|
||||||
addNewPalm(LeftHand);
|
|
||||||
addNewPalm(RightHand);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const {
|
|
||||||
return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale();
|
|
||||||
}
|
|
||||||
|
|
||||||
PalmData& HandData::addNewPalm(Hand whichHand) {
|
|
||||||
QWriteLocker locker(&_palmsLock);
|
|
||||||
_palms.push_back(PalmData(this, whichHand));
|
|
||||||
return _palms.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
PalmData HandData::getCopyOfPalmData(Hand hand) const {
|
|
||||||
QReadLocker locker(&_palmsLock);
|
|
||||||
|
|
||||||
// the palms are not necessarily added in left-right order,
|
|
||||||
// so we have to search for the correct hand
|
|
||||||
for (const auto& palm : _palms) {
|
|
||||||
if (palm.whichHand() == hand && palm.isActive()) {
|
|
||||||
return palm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return PalmData(); // invalid hand
|
|
||||||
}
|
|
||||||
|
|
||||||
PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) :
|
|
||||||
_rawRotation(0.0f, 0.0f, 0.0f, 1.0f),
|
|
||||||
_rawPosition(0.0f),
|
|
||||||
_rawVelocity(0.0f),
|
|
||||||
_rawAngularVelocity(0.0f),
|
|
||||||
_totalPenetration(0.0f),
|
|
||||||
_isActive(false),
|
|
||||||
_numFramesWithoutData(0),
|
|
||||||
_owningHandData(owningHandData),
|
|
||||||
_hand(hand) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void PalmData::addToPosition(const glm::vec3& delta) {
|
|
||||||
_rawPosition += _owningHandData->worldToLocalVector(delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
|
||||||
const PalmData*& collidingPalm) const {
|
|
||||||
QReadLocker locker(&_palmsLock);
|
|
||||||
|
|
||||||
for (const auto& palm : _palms) {
|
|
||||||
if (!palm.isActive()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
glm::vec3 palmPosition = palm.getPosition();
|
|
||||||
const float PALM_RADIUS = 0.05f; // in world (not voxel) coordinates
|
|
||||||
if (findSphereSpherePenetration(penetratorCenter, penetratorRadius, palmPosition, PALM_RADIUS, penetration)) {
|
|
||||||
collidingPalm = &palm;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::quat HandData::getBaseOrientation() const {
|
|
||||||
return _owningAvatarData->getOrientation();
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 HandData::getBasePosition() const {
|
|
||||||
return _owningAvatarData->getPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
float HandData::getBaseScale() const {
|
|
||||||
return _owningAvatarData->getTargetScale();
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 PalmData::getFingerDirection() const {
|
|
||||||
// finger points along yAxis in hand-frame
|
|
||||||
const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 1.0f, 0.0f);
|
|
||||||
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION));
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 PalmData::getNormal() const {
|
|
||||||
// palm normal points along zAxis in hand-frame
|
|
||||||
const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, 0.0f, 1.0f);
|
|
||||||
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
//
|
|
||||||
// HandData.h
|
|
||||||
// libraries/avatars/src
|
|
||||||
//
|
|
||||||
// Created by Eric Johnston on 6/26/13.
|
|
||||||
// Copyright 2013 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_HandData_h
|
|
||||||
#define hifi_HandData_h
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <QReadWriteLock>
|
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
|
|
||||||
class AvatarData;
|
|
||||||
class PalmData;
|
|
||||||
|
|
||||||
class HandData {
|
|
||||||
public:
|
|
||||||
enum Hand {
|
|
||||||
LeftHand,
|
|
||||||
RightHand,
|
|
||||||
UnknownHand,
|
|
||||||
NUMBER_OF_HANDS
|
|
||||||
};
|
|
||||||
|
|
||||||
HandData(AvatarData* owningAvatar);
|
|
||||||
virtual ~HandData() {}
|
|
||||||
|
|
||||||
// position conversion
|
|
||||||
glm::vec3 localToWorldPosition(const glm::vec3& localPosition) {
|
|
||||||
return getBasePosition() + getBaseOrientation() * localPosition * getBaseScale();
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 localToWorldDirection(const glm::vec3& localVector) {
|
|
||||||
return getBaseOrientation() * localVector * getBaseScale();
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const;
|
|
||||||
|
|
||||||
PalmData getCopyOfPalmData(Hand hand) const;
|
|
||||||
|
|
||||||
std::vector<PalmData> getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; }
|
|
||||||
|
|
||||||
/// Checks for penetration between the described sphere and the hand.
|
|
||||||
/// \param penetratorCenter the center of the penetration test sphere
|
|
||||||
/// \param penetratorRadius the radius of the penetration test sphere
|
|
||||||
/// \param penetration[out] the vector in which to store the penetration
|
|
||||||
/// \param collidingPalm[out] a const PalmData* to the palm that was collided with
|
|
||||||
/// \return whether or not the sphere penetrated
|
|
||||||
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
|
||||||
const PalmData*& collidingPalm) const;
|
|
||||||
|
|
||||||
glm::quat getBaseOrientation() const;
|
|
||||||
|
|
||||||
/// Allows a lamda function write access to the specific palm for this Hand, this might
|
|
||||||
/// modify the _palms vector
|
|
||||||
template<typename PalmModifierFunction> void modifyPalm(Hand whichHand, PalmModifierFunction callback);
|
|
||||||
|
|
||||||
friend class AvatarData;
|
|
||||||
protected:
|
|
||||||
AvatarData* _owningAvatarData;
|
|
||||||
std::vector<PalmData> _palms;
|
|
||||||
mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive };
|
|
||||||
|
|
||||||
glm::vec3 getBasePosition() const;
|
|
||||||
float getBaseScale() const;
|
|
||||||
|
|
||||||
PalmData& addNewPalm(Hand whichHand);
|
|
||||||
PalmData& getPalmData(Hand hand);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// privatize copy ctor and assignment operator so copies of this object cannot be made
|
|
||||||
HandData(const HandData&);
|
|
||||||
HandData& operator= (const HandData&);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class PalmData {
|
|
||||||
public:
|
|
||||||
PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand);
|
|
||||||
glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); }
|
|
||||||
glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); }
|
|
||||||
glm::vec3 getAngularVelocity() const { return _owningHandData->localToWorldDirection(_rawAngularVelocity); }
|
|
||||||
|
|
||||||
const glm::vec3& getRawPosition() const { return _rawPosition; }
|
|
||||||
bool isActive() const { return _isActive; }
|
|
||||||
bool isValid() const { return _owningHandData; }
|
|
||||||
|
|
||||||
void setActive(bool active) { _isActive = active; }
|
|
||||||
|
|
||||||
HandData::Hand whichHand() const { return _hand; }
|
|
||||||
void setHand(HandData::Hand hand) { _hand = hand; }
|
|
||||||
|
|
||||||
void setRawRotation(const glm::quat& rawRotation) { _rawRotation = rawRotation; };
|
|
||||||
glm::quat getRawRotation() const { return _rawRotation; }
|
|
||||||
glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; }
|
|
||||||
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
|
|
||||||
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
|
|
||||||
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
|
|
||||||
|
|
||||||
void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
|
|
||||||
const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
|
|
||||||
|
|
||||||
void addToPosition(const glm::vec3& delta);
|
|
||||||
|
|
||||||
void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
|
|
||||||
void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.0f); }
|
|
||||||
|
|
||||||
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
|
|
||||||
const glm::vec3 getTipPosition() const { return _owningHandData->localToWorldPosition(_tipPosition); }
|
|
||||||
const glm::vec3& getTipRawPosition() const { return _tipPosition; }
|
|
||||||
|
|
||||||
void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; }
|
|
||||||
const glm::vec3 getTipVelocity() const { return _owningHandData->localToWorldDirection(_tipVelocity); }
|
|
||||||
const glm::vec3& getTipRawVelocity() const { return _tipVelocity; }
|
|
||||||
|
|
||||||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
|
||||||
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
|
|
||||||
int getFramesWithoutData() const { return _numFramesWithoutData; }
|
|
||||||
|
|
||||||
// FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information
|
|
||||||
// from an action and/or the UserInputMapper instead of piping it through here.
|
|
||||||
void setTrigger(float trigger) { _trigger = trigger; }
|
|
||||||
float getTrigger() const { return _trigger; }
|
|
||||||
|
|
||||||
// return world-frame:
|
|
||||||
glm::vec3 getFingerDirection() const;
|
|
||||||
glm::vec3 getNormal() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// unless marked otherwise, these are all in the model-frame
|
|
||||||
glm::quat _rawRotation;
|
|
||||||
glm::vec3 _rawPosition;
|
|
||||||
glm::vec3 _rawVelocity;
|
|
||||||
glm::vec3 _rawAngularVelocity;
|
|
||||||
glm::quat _rawDeltaRotation;
|
|
||||||
glm::quat _lastRotation;
|
|
||||||
|
|
||||||
glm::vec3 _tipPosition;
|
|
||||||
glm::vec3 _tipVelocity;
|
|
||||||
glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations
|
|
||||||
|
|
||||||
float _trigger;
|
|
||||||
|
|
||||||
bool _isActive; /// This has current valid data
|
|
||||||
int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost.
|
|
||||||
HandData* _owningHandData;
|
|
||||||
HandData::Hand _hand;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename PalmModifierFunction> void HandData::modifyPalm(Hand whichHand, PalmModifierFunction callback) {
|
|
||||||
QReadLocker locker(&_palmsLock);
|
|
||||||
for (auto& palm : _palms) {
|
|
||||||
if (palm.whichHand() == whichHand && palm.isValid()) {
|
|
||||||
callback(palm);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // hifi_HandData_h
|
|
|
@ -392,13 +392,20 @@ void AnimDebugDraw::update() {
|
||||||
|
|
||||||
assert(numVerts == (v - verts));
|
assert(numVerts == (v - verts));
|
||||||
|
|
||||||
|
// The RenderItem culling is not working correctly
|
||||||
|
// Workaround this issue by using the default constructed
|
||||||
|
// item._bound which is a 16 km cube.
|
||||||
|
/*
|
||||||
render::Item::Bound theBound;
|
render::Item::Bound theBound;
|
||||||
for (int i = 0; i < numVerts; i++) {
|
for (int i = 0; i < numVerts; i++) {
|
||||||
theBound += verts[i].pos;
|
theBound += verts[i].pos;
|
||||||
}
|
}
|
||||||
|
data._bound = theBound;
|
||||||
|
*/
|
||||||
|
|
||||||
data._isVisible = (numVerts > 0);
|
data._isVisible = (numVerts > 0);
|
||||||
data._bound = theBound;
|
|
||||||
|
|
||||||
data._indexBuffer->resize(sizeof(uint16_t) * numVerts);
|
data._indexBuffer->resize(sizeof(uint16_t) * numVerts);
|
||||||
uint16_t* indices = (uint16_t*)data._indexBuffer->editData();
|
uint16_t* indices = (uint16_t*)data._indexBuffer->editData();
|
||||||
for (int i = 0; i < numVerts; i++) {
|
for (int i = 0; i < numVerts; i++) {
|
||||||
|
|
|
@ -115,9 +115,6 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
|
||||||
auto scriptContent = _scriptCache[url];
|
auto scriptContent = _scriptCache[url];
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
qCDebug(scriptengine) << "Found script in cache:" << url.toString();
|
qCDebug(scriptengine) << "Found script in cache:" << url.toString();
|
||||||
#if 1 // def THREAD_DEBUGGING
|
|
||||||
qCDebug(scriptengine) << "ScriptCache::getScriptContents() about to call contentAvailable() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
|
|
||||||
#endif
|
|
||||||
contentAvailable(url.toString(), scriptContent, true, true);
|
contentAvailable(url.toString(), scriptContent, true, true);
|
||||||
} else {
|
} else {
|
||||||
bool alreadyWaiting = _contentCallbacks.contains(url);
|
bool alreadyWaiting = _contentCallbacks.contains(url);
|
||||||
|
|
|
@ -471,3 +471,11 @@ bool isNaN(glm::quat value) {
|
||||||
return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z);
|
return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::mat4 orthoInverse(const glm::mat4& m) {
|
||||||
|
glm::mat4 r = m;
|
||||||
|
r[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
r = glm::transpose(r);
|
||||||
|
r[3] = -(r * m[3]);
|
||||||
|
r[3][3] = 1.0f;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
|
@ -232,4 +232,6 @@ glm::vec2 getFacingDir2D(const glm::mat4& m);
|
||||||
bool isNaN(glm::vec3 value);
|
bool isNaN(glm::vec3 value);
|
||||||
bool isNaN(glm::quat value);
|
bool isNaN(glm::quat value);
|
||||||
|
|
||||||
|
glm::mat4 orthoInverse(const glm::mat4& m);
|
||||||
|
|
||||||
#endif // hifi_GLMHelpers_h
|
#endif // hifi_GLMHelpers_h
|
||||||
|
|
|
@ -104,6 +104,7 @@ void updateQmlItemFromAction(QObject* target, QAction* source) {
|
||||||
target->setProperty("checkable", source->isCheckable());
|
target->setProperty("checkable", source->isCheckable());
|
||||||
target->setProperty("enabled", source->isEnabled());
|
target->setProperty("enabled", source->isEnabled());
|
||||||
target->setProperty("text", source->text());
|
target->setProperty("text", source->text());
|
||||||
|
target->setProperty("shortcut", source->shortcut().toString());
|
||||||
target->setProperty("checked", source->isChecked());
|
target->setProperty("checked", source->isChecked());
|
||||||
target->setProperty("visible", source->isVisible());
|
target->setProperty("visible", source->isVisible());
|
||||||
}
|
}
|
||||||
|
@ -190,6 +191,20 @@ void VrMenu::addAction(QMenu* menu, QAction* action) {
|
||||||
bindActionToQmlAction(result, action);
|
bindActionToQmlAction(result, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VrMenu::addSeparator(QMenu* menu) {
|
||||||
|
Q_ASSERT(MenuUserData::forObject(menu));
|
||||||
|
MenuUserData* userData = MenuUserData::forObject(menu);
|
||||||
|
if (!userData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QObject* menuQml = findMenuObject(userData->uuid.toString());
|
||||||
|
Q_ASSERT(menuQml);
|
||||||
|
|
||||||
|
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addSeparator", Qt::DirectConnection);
|
||||||
|
Q_ASSERT(invokeResult);
|
||||||
|
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
|
||||||
|
}
|
||||||
|
|
||||||
void VrMenu::insertAction(QAction* before, QAction* action) {
|
void VrMenu::insertAction(QAction* before, QAction* action) {
|
||||||
QObject* beforeQml{ nullptr };
|
QObject* beforeQml{ nullptr };
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,6 +28,7 @@ public:
|
||||||
VrMenu(QObject* parent = nullptr);
|
VrMenu(QObject* parent = nullptr);
|
||||||
void addMenu(QMenu* menu);
|
void addMenu(QMenu* menu);
|
||||||
void addAction(QMenu* parent, QAction* action);
|
void addAction(QMenu* parent, QAction* action);
|
||||||
|
void addSeparator(QMenu* parent);
|
||||||
void insertAction(QAction* before, QAction* action);
|
void insertAction(QAction* before, QAction* action);
|
||||||
void removeAction(QAction* action);
|
void removeAction(QAction* action);
|
||||||
|
|
||||||
|
|
|
@ -511,7 +511,11 @@ void MenuWrapper::setEnabled(bool enabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QAction* MenuWrapper::addSeparator() {
|
QAction* MenuWrapper::addSeparator() {
|
||||||
return _realMenu->addSeparator();
|
QAction* action = _realMenu->addSeparator();
|
||||||
|
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||||
|
vrMenu->addSeparator(_realMenu);
|
||||||
|
});
|
||||||
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuWrapper::addAction(QAction* action) {
|
void MenuWrapper::addAction(QAction* action) {
|
||||||
|
|
Loading…
Reference in a new issue