mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 02:30:14 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
d960a7c42e
24 changed files with 927 additions and 688 deletions
2
BUILD.md
2
BUILD.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
- [cmake](https://cmake.org/download/): 3.9
|
||||
- [Qt](https://www.qt.io/download-open-source): 5.9.1
|
||||
- [OpenSSL](https://www.openssl.org/): Use the latest available version of OpenSSL to avoid security vulnerabilities.
|
||||
- [OpenSSL](https://www.openssl.org/): Use the latest available 1.0 version (**NOT** 1.1) of OpenSSL to avoid security vulnerabilities.
|
||||
- [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
|
||||
|
||||
### CMake External Project Dependencies
|
||||
|
|
|
@ -272,22 +272,22 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
|
||||
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
|
||||
} else {
|
||||
// this node is an agent
|
||||
const QHostAddress& addr = node->getLocalSocket().getAddress();
|
||||
bool isLocalUser = (addr == limitedNodeList->getLocalSockAddr().getAddress() ||
|
||||
addr == QHostAddress::LocalHost);
|
||||
|
||||
// at this point we don't have a sending socket for packets from this node - assume it is the active socket
|
||||
// or the public socket if we haven't activated a socket for the node yet
|
||||
HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket();
|
||||
|
||||
QString hardwareAddress;
|
||||
QUuid machineFingerprint;
|
||||
bool isLocalUser { false };
|
||||
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
hardwareAddress = nodeData->getHardwareAddress();
|
||||
machineFingerprint = nodeData->getMachineFingerprint();
|
||||
|
||||
auto sendingAddress = nodeData->getSendingSockAddr().getAddress();
|
||||
isLocalUser = (sendingAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
|
||||
sendingAddress == QHostAddress::LocalHost);
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
},
|
||||
|
||||
{ "from": "Standard.RX",
|
||||
"when": [ "Application.InHMD", "Application.SnapTurn" ],
|
||||
"when": [ "Application.SnapTurn" ],
|
||||
"to": "Actions.StepYaw",
|
||||
"filters":
|
||||
[
|
||||
|
@ -128,4 +128,4 @@
|
|||
{ "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" },
|
||||
{ "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" }
|
||||
]
|
||||
}
|
||||
}
|
113
interface/resources/qml/controls-uit/CheckBoxQQC2.qml
Normal file
113
interface/resources/qml/controls-uit/CheckBoxQQC2.qml
Normal file
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// CheckBox2.qml
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 10 Aug 2017
|
||||
// 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.7
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
import "../styles-uit"
|
||||
import "../controls-uit" as HiFiControls
|
||||
|
||||
CheckBox {
|
||||
id: checkBox
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
padding: 0
|
||||
leftPadding: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property string color: hifi.colors.lightGrayText
|
||||
readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light
|
||||
property bool isRedCheck: false
|
||||
property bool isRound: false
|
||||
property int boxSize: 14
|
||||
property int boxRadius: isRound ? boxSize : 3
|
||||
property bool wrap: true;
|
||||
readonly property int checkSize: Math.max(boxSize - 8, 10)
|
||||
readonly property int checkRadius: isRound ? checkSize / 2 : 2
|
||||
focusPolicy: Qt.ClickFocus
|
||||
|
||||
indicator: Rectangle {
|
||||
id: box
|
||||
implicitWidth: boxSize
|
||||
implicitHeight: boxSize
|
||||
radius: boxRadius
|
||||
x: checkBox.leftPadding
|
||||
y: parent.height / 2 - height / 2
|
||||
border.width: 1
|
||||
border.color: pressed || hovered
|
||||
? hifi.colors.checkboxCheckedBorder
|
||||
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.2
|
||||
color: pressed || hovered
|
||||
? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart)
|
||||
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart)
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: pressed || hovered
|
||||
? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish)
|
||||
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: pressed || hovered
|
||||
anchors.centerIn: parent
|
||||
id: innerBox
|
||||
width: checkSize - 4
|
||||
height: width
|
||||
radius: checkRadius
|
||||
color: hifi.colors.checkboxCheckedBorder
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: check
|
||||
width: checkSize
|
||||
height: checkSize
|
||||
radius: checkRadius
|
||||
anchors.centerIn: parent
|
||||
color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked
|
||||
border.width: 2
|
||||
border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder
|
||||
visible: checked && !pressed || !checked && pressed
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: disabledOverlay
|
||||
visible: !enabled
|
||||
width: boxSize
|
||||
height: boxSize
|
||||
radius: boxRadius
|
||||
border.width: 1
|
||||
border.color: hifi.colors.baseGrayHighlight
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
id: root
|
||||
FontLoader { id: ralewaySemiBold; source: pathToFonts + "fonts/Raleway-SemiBold.ttf"; }
|
||||
font.pixelSize: hifi.fontSizes.inputLabel
|
||||
font.family: ralewaySemiBold.name
|
||||
text: checkBox.text
|
||||
color: checkBox.color
|
||||
x: 2
|
||||
wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap
|
||||
elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight
|
||||
enabled: checkBox.enabled
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: checkBox.indicator.width + checkBox.spacing
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "../../styles-uit"
|
||||
|
@ -36,7 +36,41 @@ Rectangle {
|
|||
return (root.parent !== null) && root.parent.objectName == "loader";
|
||||
}
|
||||
|
||||
|
||||
property bool isVR: Audio.context === "VR"
|
||||
property real rightMostInputLevelPos: 0
|
||||
//placeholder for control sizes and paddings
|
||||
//recalculates dynamically in case of UI size is changed
|
||||
QtObject {
|
||||
id: margins
|
||||
property real paddings: root.width / 20.25
|
||||
|
||||
property real sizeCheckBox: root.width / 13.5
|
||||
property real sizeText: root.width / 2.5
|
||||
property real sizeLevel: root.width / 5.8
|
||||
property real sizeDesktop: root.width / 5.8
|
||||
property real sizeVR: root.width / 13.5
|
||||
}
|
||||
|
||||
TabBar {
|
||||
id: bar
|
||||
spacing: 0
|
||||
width: parent.width
|
||||
height: 42
|
||||
currentIndex: isVR ? 1 : 0
|
||||
|
||||
AudioControls.AudioTabButton {
|
||||
height: parent.height
|
||||
text: qsTr("Desktop")
|
||||
}
|
||||
AudioControls.AudioTabButton {
|
||||
height: parent.height
|
||||
text: qsTr("VR")
|
||||
}
|
||||
}
|
||||
|
||||
property bool showPeaks: true;
|
||||
|
||||
function enablePeakValues() {
|
||||
Audio.devices.input.peakValuesEnabled = true;
|
||||
Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
|
||||
|
@ -45,6 +79,7 @@ Rectangle {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function disablePeakValues() {
|
||||
root.showPeaks = false;
|
||||
Audio.devices.input.peakValuesEnabled = false;
|
||||
|
@ -55,29 +90,32 @@ Rectangle {
|
|||
onVisibleChanged: visible ? enablePeakValues() : disablePeakValues();
|
||||
|
||||
Column {
|
||||
y: 16; // padding does not work
|
||||
spacing: 16;
|
||||
spacing: 12;
|
||||
anchors.top: bar.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 5
|
||||
width: parent.width;
|
||||
|
||||
Separator { }
|
||||
|
||||
RalewayRegular {
|
||||
x: 16; // padding does not work
|
||||
x: margins.paddings + muteMic.boxSize + muteMic.spacing;
|
||||
size: 16;
|
||||
color: "white";
|
||||
text: root.title;
|
||||
|
||||
visible: root.showTitle();
|
||||
text: qsTr("Input Device Settings")
|
||||
}
|
||||
|
||||
Separator { visible: root.showTitle() }
|
||||
|
||||
ColumnLayout {
|
||||
x: 16; // padding does not work
|
||||
x: margins.paddings;
|
||||
spacing: 16;
|
||||
width: parent.width;
|
||||
|
||||
// mute is in its own row
|
||||
RowLayout {
|
||||
AudioControls.CheckBox {
|
||||
id: muteMic
|
||||
text: qsTr("Mute microphone");
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
isRedCheck: true;
|
||||
checked: Audio.muted;
|
||||
onClicked: {
|
||||
|
@ -88,8 +126,9 @@ Rectangle {
|
|||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 16;
|
||||
spacing: muteMic.spacing*2; //make it visually distinguish
|
||||
AudioControls.CheckBox {
|
||||
spacing: muteMic.spacing
|
||||
text: qsTr("Enable noise reduction");
|
||||
checked: Audio.noiseReduction;
|
||||
onClicked: {
|
||||
|
@ -98,24 +137,33 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
AudioControls.CheckBox {
|
||||
spacing: muteMic.spacing
|
||||
text: qsTr("Show audio level meter");
|
||||
checked: AvatarInputs.showAudioTools;
|
||||
onClicked: {
|
||||
AvatarInputs.showAudioTools = checked;
|
||||
checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding
|
||||
}
|
||||
onXChanged: rightMostInputLevelPos = x + width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {}
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2
|
||||
height: 36
|
||||
|
||||
HiFiGlyphs {
|
||||
width: margins.sizeCheckBox
|
||||
text: hifi.glyphs.mic;
|
||||
color: hifi.colors.primaryHighlight;
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: -size/4 //the glyph has empty space at left about 25%
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 28;
|
||||
size: 30;
|
||||
}
|
||||
RalewayRegular {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
@ -126,90 +174,114 @@ Rectangle {
|
|||
}
|
||||
|
||||
ListView {
|
||||
anchors { left: parent.left; right: parent.right; leftMargin: 70 }
|
||||
height: 125;
|
||||
spacing: 0;
|
||||
id: inputView
|
||||
width: parent.width - margins.paddings*2
|
||||
x: margins.paddings
|
||||
height: Math.min(150, contentHeight);
|
||||
spacing: 4;
|
||||
snapMode: ListView.SnapToItem;
|
||||
clip: true;
|
||||
model: Audio.devices.input;
|
||||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: 36;
|
||||
|
||||
width: rightMostInputLevelPos
|
||||
height: margins.sizeCheckBox > checkBoxInput.implicitHeight ?
|
||||
margins.sizeCheckBox : checkBoxInput.implicitHeight
|
||||
|
||||
AudioControls.CheckBox {
|
||||
id: checkbox
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
id: checkBoxInput
|
||||
anchors.left: parent.left
|
||||
text: display;
|
||||
wrap: false;
|
||||
checked: selected;
|
||||
enabled: false;
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - inputLevel.width
|
||||
clip: true
|
||||
checkable: !checked
|
||||
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
|
||||
boxSize: margins.sizeCheckBox / 2
|
||||
isRound: true
|
||||
text: devicename
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
Audio.setInputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: checkbox
|
||||
onClicked: Audio.setInputDevice(info);
|
||||
}
|
||||
|
||||
InputPeak {
|
||||
id: inputPeak;
|
||||
visible: Audio.devices.input.peakValuesAvailable;
|
||||
id: inputLevel
|
||||
anchors.right: parent.right
|
||||
peak: model.peak;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
visible: (bar.currentIndex === 1 && selectedHMD && isVR) ||
|
||||
(bar.currentIndex === 0 && selectedDesktop && !isVR) &&
|
||||
Audio.devices.input.peakValuesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {}
|
||||
|
||||
RowLayout {
|
||||
Column {
|
||||
RowLayout {
|
||||
HiFiGlyphs {
|
||||
text: hifi.glyphs.unmuted;
|
||||
color: hifi.colors.primaryHighlight;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 36;
|
||||
}
|
||||
RalewayRegular {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 16;
|
||||
color: hifi.colors.lightGrayText;
|
||||
text: qsTr("CHOOSE OUTPUT DEVICE");
|
||||
}
|
||||
}
|
||||
Item {
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2
|
||||
height: 36
|
||||
|
||||
PlaySampleSound { anchors { left: parent.left; leftMargin: 60 }}
|
||||
HiFiGlyphs {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: -size/4 //the glyph has empty space at left about 25%
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: margins.sizeCheckBox
|
||||
text: hifi.glyphs.unmuted;
|
||||
color: hifi.colors.primaryHighlight;
|
||||
size: 36;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
width: margins.sizeText + margins.sizeLevel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: margins.sizeCheckBox
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 16;
|
||||
color: hifi.colors.lightGrayText;
|
||||
text: qsTr("CHOOSE OUTPUT DEVICE");
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
anchors { left: parent.left; right: parent.right; leftMargin: 70 }
|
||||
height: Math.min(250, contentHeight);
|
||||
spacing: 0;
|
||||
id: outputView
|
||||
width: parent.width - margins.paddings*2
|
||||
x: margins.paddings
|
||||
height: Math.min(360 - inputView.height, contentHeight);
|
||||
spacing: 4;
|
||||
snapMode: ListView.SnapToItem;
|
||||
clip: true;
|
||||
model: Audio.devices.output;
|
||||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: 36;
|
||||
width: rightMostInputLevelPos
|
||||
height: margins.sizeCheckBox > checkBoxOutput.implicitHeight ?
|
||||
margins.sizeCheckBox : checkBoxOutput.implicitHeight
|
||||
|
||||
AudioControls.CheckBox {
|
||||
id: checkbox
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
text: display;
|
||||
checked: selected;
|
||||
enabled: false;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: checkbox
|
||||
onClicked: Audio.setOutputDevice(info);
|
||||
id: checkBoxOutput
|
||||
width: parent.width
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
boxSize: margins.sizeCheckBox / 2
|
||||
isRound: true
|
||||
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
|
||||
checkable: !checked
|
||||
text: devicename
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
Audio.setOutputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PlaySampleSound {
|
||||
x: margins.paddings
|
||||
|
||||
visible: (bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR);
|
||||
anchors { left: parent.left; leftMargin: margins.paddings }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
35
interface/resources/qml/hifi/audio/AudioTabButton.qml
Normal file
35
interface/resources/qml/hifi/audio/AudioTabButton.qml
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// AudioTabButton.qml
|
||||
// qml/hifi/audio
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 8/16/2017
|
||||
// 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.7
|
||||
import QtQuick.Controls 2.2
|
||||
import "../../controls-uit" as HifiControls
|
||||
import "../../styles-uit"
|
||||
|
||||
TabButton {
|
||||
id: control
|
||||
font.pixelSize: height / 2
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
contentItem: RalewaySemiBold {
|
||||
text: control.text
|
||||
font: control.font
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: control.checked ? hifi.colors.baseGray : "black"
|
||||
}
|
||||
}
|
|
@ -9,10 +9,10 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick 2.7
|
||||
|
||||
import "../../controls-uit" as HifiControls
|
||||
|
||||
HifiControls.CheckBox {
|
||||
HifiControls.CheckBoxQQC2 {
|
||||
color: "white"
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
Text {
|
||||
id: root
|
||||
|
|
|
@ -2057,6 +2057,7 @@ void Application::cleanupBeforeQuit() {
|
|||
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine
|
||||
DependencyManager::destroy<AudioClient>();
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
|
||||
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
|
||||
}
|
||||
|
@ -5159,12 +5160,6 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("overlays");
|
||||
_overlays.update(deltaTime);
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(app, "RayPickManager");
|
||||
_rayPickManager.update();
|
||||
|
@ -5175,6 +5170,12 @@ void Application::update(float deltaTime) {
|
|||
_laserPointerManager.update();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("overlays");
|
||||
_overlays.update(deltaTime);
|
||||
}
|
||||
|
||||
// Update _viewFrustum with latest camera and view frustum data...
|
||||
// NOTE: we get this from the view frustum, to make it simpler, since the
|
||||
// loadViewFrumstum() method will get the correct details from the camera
|
||||
|
|
|
@ -86,7 +86,9 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
|
|||
|
||||
void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) {
|
||||
if (!id.isNull() && props.isValid()) {
|
||||
qApp->getOverlays().editOverlay(id, props);
|
||||
QVariantMap propMap = props.toMap();
|
||||
propMap.remove("visible");
|
||||
qApp->getOverlays().editOverlay(id, propMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ const RenderState LaserPointerScriptingInterface::buildRenderState(const QVarian
|
|||
if (propMap["start"].isValid()) {
|
||||
QVariantMap startMap = propMap["start"].toMap();
|
||||
if (startMap["type"].isValid()) {
|
||||
startMap.remove("visible");
|
||||
startID = qApp->getOverlays().addOverlay(startMap["type"].toString(), startMap);
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +105,7 @@ const RenderState LaserPointerScriptingInterface::buildRenderState(const QVarian
|
|||
QVariantMap pathMap = propMap["path"].toMap();
|
||||
// right now paths must be line3ds
|
||||
if (pathMap["type"].isValid() && pathMap["type"].toString() == "line3d") {
|
||||
pathMap.remove("visible");
|
||||
pathID = qApp->getOverlays().addOverlay(pathMap["type"].toString(), pathMap);
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +114,7 @@ const RenderState LaserPointerScriptingInterface::buildRenderState(const QVarian
|
|||
if (propMap["end"].isValid()) {
|
||||
QVariantMap endMap = propMap["end"].toMap();
|
||||
if (endMap["type"].isValid()) {
|
||||
endMap.remove("visible");
|
||||
endID = qApp->getOverlays().addOverlay(endMap["type"].toString(), endMap);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,10 +135,10 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) {
|
|||
DependencyManager::get<AudioClient>()->setReverbOptions(options);
|
||||
}
|
||||
|
||||
void Audio::setInputDevice(const QAudioDeviceInfo& device) {
|
||||
_devices.chooseInputDevice(device);
|
||||
void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
_devices.chooseInputDevice(device, isHMD);
|
||||
}
|
||||
|
||||
void Audio::setOutputDevice(const QAudioDeviceInfo& device) {
|
||||
_devices.chooseOutputDevice(device);
|
||||
void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
_devices.chooseOutputDevice(device, isHMD);
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ public:
|
|||
void showMicMeter(bool show);
|
||||
void setInputVolume(float volume);
|
||||
|
||||
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device);
|
||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device);
|
||||
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
Q_INVOKABLE void setReverb(bool enable);
|
||||
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
|
|
|
@ -38,15 +38,17 @@ Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
|
|||
}
|
||||
|
||||
enum AudioDeviceRole {
|
||||
DisplayRole = Qt::DisplayRole,
|
||||
CheckStateRole = Qt::CheckStateRole,
|
||||
PeakRole = Qt::UserRole,
|
||||
InfoRole = Qt::UserRole + 1
|
||||
DeviceNameRole = Qt::UserRole,
|
||||
SelectedDesktopRole,
|
||||
SelectedHMDRole,
|
||||
PeakRole,
|
||||
InfoRole
|
||||
};
|
||||
|
||||
QHash<int, QByteArray> AudioDeviceList::_roles {
|
||||
{ DisplayRole, "display" },
|
||||
{ CheckStateRole, "selected" },
|
||||
{ DeviceNameRole, "devicename" },
|
||||
{ SelectedDesktopRole, "selectedDesktop" },
|
||||
{ SelectedHMDRole, "selectedHMD" },
|
||||
{ PeakRole, "peak" },
|
||||
{ InfoRole, "info" }
|
||||
};
|
||||
|
@ -68,15 +70,64 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) {
|
|||
|
||||
Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled };
|
||||
|
||||
AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) {
|
||||
auto& setting1 = getSetting(true, QAudio::AudioInput);
|
||||
if (setting1.isSet()) {
|
||||
qDebug() << "Device name in settings for HMD, Input" << setting1.get();
|
||||
} else {
|
||||
qDebug() << "Device name in settings for HMD, Input not set";
|
||||
}
|
||||
|
||||
auto& setting2 = getSetting(true, QAudio::AudioOutput);
|
||||
if (setting2.isSet()) {
|
||||
qDebug() << "Device name in settings for HMD, Output" << setting2.get();
|
||||
} else {
|
||||
qDebug() << "Device name in settings for HMD, Output not set";
|
||||
}
|
||||
|
||||
auto& setting3 = getSetting(false, QAudio::AudioInput);
|
||||
if (setting3.isSet()) {
|
||||
qDebug() << "Device name in settings for Desktop, Input" << setting3.get();
|
||||
} else {
|
||||
qDebug() << "Device name in settings for Desktop, Input not set";
|
||||
}
|
||||
|
||||
auto& setting4 = getSetting(false, QAudio::AudioOutput);
|
||||
if (setting4.isSet()) {
|
||||
qDebug() << "Device name in settings for Desktop, Output" << setting4.get();
|
||||
} else {
|
||||
qDebug() << "Device name in settings for Desktop, Output not set";
|
||||
}
|
||||
}
|
||||
|
||||
AudioDeviceList::~AudioDeviceList() {
|
||||
//save all selected devices
|
||||
auto& settingHMD = getSetting(true, _mode);
|
||||
auto& settingDesktop = getSetting(false, _mode);
|
||||
// store the selected device
|
||||
foreach(std::shared_ptr<AudioDevice> adevice, _devices) {
|
||||
if (adevice->selectedDesktop) {
|
||||
qDebug() << "Saving Desktop for" << _mode << "name" << adevice->info.deviceName();
|
||||
settingDesktop.set(adevice->info.deviceName());
|
||||
}
|
||||
if (adevice->selectedHMD) {
|
||||
qDebug() << "Saving HMD for" << _mode << "name" << adevice->info.deviceName();
|
||||
settingHMD.set(adevice->info.deviceName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid() || index.row() >= rowCount()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role == DisplayRole) {
|
||||
if (role == DeviceNameRole) {
|
||||
return _devices.at(index.row())->display;
|
||||
} else if (role == CheckStateRole) {
|
||||
return _devices.at(index.row())->selected;
|
||||
} else if (role == SelectedDesktopRole) {
|
||||
return _devices.at(index.row())->selectedDesktop;
|
||||
} else if (role == SelectedHMDRole) {
|
||||
return _devices.at(index.row())->selectedHMD;
|
||||
} else if (role == InfoRole) {
|
||||
return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row())->info);
|
||||
} else {
|
||||
|
@ -130,37 +181,48 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
|
||||
auto oldDevice = _selectedDevice;
|
||||
_selectedDevice = device;
|
||||
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
auto oldDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
selectedDevice = device;
|
||||
|
||||
for (auto i = 0; i < rowCount(); ++i) {
|
||||
AudioDevice& device = *_devices[i];
|
||||
|
||||
if (device.selected && device.info != _selectedDevice) {
|
||||
device.selected = false;
|
||||
} else if (device.info == _selectedDevice) {
|
||||
device.selected = true;
|
||||
for (auto i = 0; i < _devices.size(); ++i) {
|
||||
std::shared_ptr<AudioDevice> device = _devices[i];
|
||||
bool &isSelected = isHMD ? device->selectedHMD : device->selectedDesktop;
|
||||
if (isSelected && device->info != selectedDevice) {
|
||||
isSelected = false;
|
||||
} else if (device->info == selectedDevice) {
|
||||
isSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
emit deviceChanged(_selectedDevice);
|
||||
emit deviceChanged(selectedDevice);
|
||||
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
|
||||
}
|
||||
|
||||
void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
|
||||
void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices, bool isHMD) {
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
|
||||
const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName;
|
||||
beginResetModel();
|
||||
|
||||
_devices.clear();
|
||||
|
||||
foreach(const QAudioDeviceInfo& deviceInfo, devices) {
|
||||
AudioDevice device;
|
||||
bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
|
||||
device.info = deviceInfo;
|
||||
device.display = device.info.deviceName()
|
||||
.replace("High Definition", "HD")
|
||||
.remove("Device")
|
||||
.replace(" )", ")");
|
||||
device.selected = (device.info == _selectedDevice);
|
||||
if (!selectedDevice.isNull()) {
|
||||
isSelected = (device.info == selectedDevice);
|
||||
} else {
|
||||
//no selected device for context. fallback to saved
|
||||
isSelected = (device.info.deviceName() == savedDeviceName);
|
||||
}
|
||||
qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode;
|
||||
_devices.push_back(newDevice(device));
|
||||
}
|
||||
|
||||
|
@ -203,22 +265,32 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
|
|||
connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection);
|
||||
connect(client.data(), &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection);
|
||||
|
||||
_inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput), contextIsHMD);
|
||||
_outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput), contextIsHMD);
|
||||
|
||||
// connections are made after client is initialized, so we must also fetch the devices
|
||||
_inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput));
|
||||
_outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput));
|
||||
_inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput));
|
||||
_outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput));
|
||||
const QList<QAudioDeviceInfo>& devicesInput = client->getAudioDevices(QAudio::AudioInput);
|
||||
const QList<QAudioDeviceInfo>& devicesOutput = client->getAudioDevices(QAudio::AudioOutput);
|
||||
//setup HMD devices
|
||||
_inputs.onDevicesChanged(devicesInput, true);
|
||||
_outputs.onDevicesChanged(devicesOutput, true);
|
||||
//setup Desktop devices
|
||||
_inputs.onDevicesChanged(devicesInput, false);
|
||||
_outputs.onDevicesChanged(devicesOutput, false);
|
||||
}
|
||||
|
||||
AudioDevices::~AudioDevices() {}
|
||||
|
||||
void AudioDevices::onContextChanged(const QString& context) {
|
||||
_inputs.resetDevice(_contextIsHMD);
|
||||
_outputs.resetDevice(_contextIsHMD);
|
||||
}
|
||||
|
||||
void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
|
||||
void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device,
|
||||
const QAudioDeviceInfo& previousDevice, bool isHMD) {
|
||||
QString deviceName = device.isNull() ? QString() : device.deviceName();
|
||||
|
||||
auto& setting = getSetting(_contextIsHMD, mode);
|
||||
auto& setting = getSetting(isHMD, mode);
|
||||
|
||||
// check for a previous device
|
||||
auto wasDefault = setting.get().isNull();
|
||||
|
@ -254,42 +326,94 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d
|
|||
void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) {
|
||||
if (mode == QAudio::AudioInput) {
|
||||
if (_requestedInputDevice == device) {
|
||||
onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice);
|
||||
onDeviceSelected(QAudio::AudioInput, device,
|
||||
_contextIsHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice,
|
||||
_contextIsHMD);
|
||||
_requestedInputDevice = QAudioDeviceInfo();
|
||||
}
|
||||
_inputs.onDeviceChanged(device);
|
||||
_inputs.onDeviceChanged(device, _contextIsHMD);
|
||||
} else { // if (mode == QAudio::AudioOutput)
|
||||
if (_requestedOutputDevice == device) {
|
||||
onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice);
|
||||
onDeviceSelected(QAudio::AudioOutput, device,
|
||||
_contextIsHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice,
|
||||
_contextIsHMD);
|
||||
_requestedOutputDevice = QAudioDeviceInfo();
|
||||
}
|
||||
_outputs.onDeviceChanged(device);
|
||||
_outputs.onDeviceChanged(device, _contextIsHMD);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices) {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
//readout settings
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
|
||||
_inputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioInput);
|
||||
_inputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioInput);
|
||||
|
||||
//fallback to default device
|
||||
if (_inputs._desktopSavedDeviceName.isEmpty()) {
|
||||
_inputs._desktopSavedDeviceName = client->getActiveAudioDevice(QAudio::AudioInput).deviceName();
|
||||
}
|
||||
//fallback to desktop device
|
||||
if (_inputs._hmdSavedDeviceName.isEmpty()) {
|
||||
_inputs._hmdSavedDeviceName = _inputs._desktopSavedDeviceName;
|
||||
}
|
||||
|
||||
_outputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioOutput);
|
||||
_outputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioOutput);
|
||||
|
||||
if (_outputs._desktopSavedDeviceName.isEmpty()) {
|
||||
_outputs._desktopSavedDeviceName = client->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
|
||||
}
|
||||
if (_outputs._hmdSavedDeviceName.isEmpty()) {
|
||||
_outputs._hmdSavedDeviceName = _outputs._desktopSavedDeviceName;
|
||||
}
|
||||
onContextChanged(QString());
|
||||
});
|
||||
|
||||
//set devices for both contexts
|
||||
if (mode == QAudio::AudioInput) {
|
||||
_inputs.onDevicesChanged(devices);
|
||||
_inputs.onDevicesChanged(devices, _contextIsHMD);
|
||||
_inputs.onDevicesChanged(devices, !_contextIsHMD);
|
||||
} else { // if (mode == QAudio::AudioOutput)
|
||||
_outputs.onDevicesChanged(devices);
|
||||
_outputs.onDevicesChanged(devices, _contextIsHMD);
|
||||
_outputs.onDevicesChanged(devices, !_contextIsHMD);
|
||||
}
|
||||
std::call_once(once, [&] { onContextChanged(QString()); });
|
||||
}
|
||||
|
||||
|
||||
void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device) {
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
_requestedInputDevice = device;
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
|
||||
Q_ARG(QAudio::Mode, QAudio::AudioInput),
|
||||
Q_ARG(const QAudioDeviceInfo&, device));
|
||||
void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
//check if current context equals device to change
|
||||
if (_contextIsHMD == isHMD) {
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
_requestedInputDevice = device;
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
|
||||
Q_ARG(QAudio::Mode, QAudio::AudioInput),
|
||||
Q_ARG(const QAudioDeviceInfo&, device));
|
||||
} else {
|
||||
//context is different. just save device in settings
|
||||
onDeviceSelected(QAudio::AudioInput, device,
|
||||
isHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice,
|
||||
isHMD);
|
||||
_inputs.onDeviceChanged(device, isHMD);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device) {
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
_requestedOutputDevice = device;
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
|
||||
Q_ARG(QAudio::Mode, QAudio::AudioOutput),
|
||||
Q_ARG(const QAudioDeviceInfo&, device));
|
||||
void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
//check if current context equals device to change
|
||||
if (_contextIsHMD == isHMD) {
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
_requestedOutputDevice = device;
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
|
||||
Q_ARG(QAudio::Mode, QAudio::AudioOutput),
|
||||
Q_ARG(const QAudioDeviceInfo&, device));
|
||||
} else {
|
||||
//context is different. just save device in settings
|
||||
onDeviceSelected(QAudio::AudioOutput, device,
|
||||
isHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice,
|
||||
isHMD);
|
||||
_outputs.onDeviceChanged(device, isHMD);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,15 +25,16 @@ class AudioDevice {
|
|||
public:
|
||||
QAudioDeviceInfo info;
|
||||
QString display;
|
||||
bool selected { false };
|
||||
bool selectedDesktop { false };
|
||||
bool selectedHMD { false };
|
||||
};
|
||||
|
||||
class AudioDeviceList : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput) : _mode(mode) {}
|
||||
~AudioDeviceList() = default;
|
||||
AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput);
|
||||
virtual ~AudioDeviceList();
|
||||
|
||||
virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device)
|
||||
{ return std::make_shared<AudioDevice>(device); }
|
||||
|
@ -52,8 +53,8 @@ signals:
|
|||
void deviceChanged(const QAudioDeviceInfo& device);
|
||||
|
||||
protected slots:
|
||||
void onDeviceChanged(const QAudioDeviceInfo& device);
|
||||
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices);
|
||||
void onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD);
|
||||
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices, bool isHMD);
|
||||
|
||||
protected:
|
||||
friend class AudioDevices;
|
||||
|
@ -61,8 +62,11 @@ protected:
|
|||
static QHash<int, QByteArray> _roles;
|
||||
static Qt::ItemFlags _flags;
|
||||
const QAudio::Mode _mode;
|
||||
QAudioDeviceInfo _selectedDevice;
|
||||
QAudioDeviceInfo _selectedDesktopDevice;
|
||||
QAudioDeviceInfo _selectedHMDDevice;
|
||||
QList<std::shared_ptr<AudioDevice>> _devices;
|
||||
QString _hmdSavedDeviceName;
|
||||
QString _desktopSavedDeviceName;
|
||||
};
|
||||
|
||||
class AudioInputDevice : public AudioDevice {
|
||||
|
@ -102,7 +106,6 @@ protected:
|
|||
void setPeakValuesEnabled(bool enable);
|
||||
bool _peakValuesEnabled { false };
|
||||
};
|
||||
|
||||
class Audio;
|
||||
|
||||
class AudioDevices : public QObject {
|
||||
|
@ -112,15 +115,18 @@ class AudioDevices : public QObject {
|
|||
|
||||
public:
|
||||
AudioDevices(bool& contextIsHMD);
|
||||
void chooseInputDevice(const QAudioDeviceInfo& device);
|
||||
void chooseOutputDevice(const QAudioDeviceInfo& device);
|
||||
virtual ~AudioDevices();
|
||||
|
||||
void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
signals:
|
||||
void nop();
|
||||
|
||||
private slots:
|
||||
void onContextChanged(const QString& context);
|
||||
void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice);
|
||||
void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device,
|
||||
const QAudioDeviceInfo& previousDevice, bool isHMD);
|
||||
void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
|
||||
void onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);
|
||||
|
||||
|
|
|
@ -1725,14 +1725,6 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
|
|||
if (persist) {
|
||||
_outputBufferSizeFrames.set(numFrames);
|
||||
}
|
||||
|
||||
if (_audioOutput) {
|
||||
// The buffer size can't be adjusted after QAudioOutput::start() has been called, so
|
||||
// recreate the device by switching to the default.
|
||||
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
|
||||
qCDebug(audioclient) << __FUNCTION__ << "about to send changeDevice signal outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
|
||||
emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread.
|
||||
}
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
|
|
|
@ -140,10 +140,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
};
|
||||
|
||||
this.setIgnoreTablet = function() {
|
||||
if (HMD.tabletID !== _this.tabletID) {
|
||||
RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]);
|
||||
RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]);
|
||||
}
|
||||
RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]);
|
||||
RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]);
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
|
|
@ -16,6 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
(function() {
|
||||
var TouchEventUtils = Script.require("/~/system/libraries/touchEventUtils.js");
|
||||
var halfPath = {
|
||||
type: "line3d",
|
||||
color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE,
|
||||
|
@ -88,186 +89,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
var HAPTIC_STYLUS_STRENGTH = 1.0;
|
||||
var HAPTIC_STYLUS_DURATION = 20.0;
|
||||
|
||||
function laserTargetHasKeyboardFocus(laserTarget) {
|
||||
if (laserTarget && laserTarget !== NULL_UUID) {
|
||||
return Overlays.keyboardFocusOverlay === laserTarget;
|
||||
}
|
||||
}
|
||||
|
||||
function setKeyboardFocusOnLaserTarget(laserTarget) {
|
||||
if (laserTarget && laserTarget !== NULL_UUID) {
|
||||
Overlays.keyboardFocusOverlay = laserTarget;
|
||||
Entities.keyboardFocusEntity = NULL_UUID;
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverEnterEventToLaserTarget(hand, laserTarget) {
|
||||
if (!laserTarget) {
|
||||
return;
|
||||
}
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: laserTarget.position2D,
|
||||
pos3D: laserTarget.position,
|
||||
normal: laserTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, laserTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendHoverEnterOverlay(laserTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverOverEventToLaserTarget(hand, laserTarget) {
|
||||
|
||||
if (!laserTarget) {
|
||||
return;
|
||||
}
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: laserTarget.position2D,
|
||||
pos3D: laserTarget.position,
|
||||
normal: laserTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, laserTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMouseMoveOnOverlay(laserTarget.overlayID, pointerEvent);
|
||||
Overlays.sendHoverOverOverlay(laserTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchStartEventToLaserTarget(hand, laserTarget) {
|
||||
if (!laserTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pointerEvent = {
|
||||
type: "Press",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: laserTarget.position2D,
|
||||
pos3D: laserTarget.position,
|
||||
normal: laserTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, laserTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMousePressOnOverlay(laserTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchEndEventToLaserTarget(hand, laserTarget) {
|
||||
if (!laserTarget) {
|
||||
return;
|
||||
}
|
||||
var pointerEvent = {
|
||||
type: "Release",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: laserTarget.position2D,
|
||||
pos3D: laserTarget.position,
|
||||
normal: laserTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, laserTarget.normal),
|
||||
button: "Primary"
|
||||
};
|
||||
|
||||
if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent);
|
||||
Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchMoveEventToLaserTarget(hand, laserTarget) {
|
||||
if (!laserTarget) {
|
||||
return;
|
||||
}
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: laserTarget.position2D,
|
||||
pos3D: laserTarget.position,
|
||||
normal: laserTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, laserTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// will return undefined if overlayID does not exist.
|
||||
function calculateLaserTargetFromOverlay(worldPos, overlayID) {
|
||||
var overlayPosition = Overlays.getProperty(overlayID, "position");
|
||||
if (overlayPosition === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// project stylusTip onto overlay plane.
|
||||
var overlayRotation = Overlays.getProperty(overlayID, "rotation");
|
||||
if (overlayRotation === undefined) {
|
||||
return null;
|
||||
}
|
||||
var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1});
|
||||
var distance = Vec3.dot(Vec3.subtract(worldPos, overlayPosition), normal);
|
||||
|
||||
// calclulate normalized position
|
||||
var invRot = Quat.inverse(overlayRotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, overlayPosition));
|
||||
var dpi = Overlays.getProperty(overlayID, "dpi");
|
||||
|
||||
var dimensions;
|
||||
if (dpi) {
|
||||
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property
|
||||
// is used as a scale.
|
||||
var resolution = Overlays.getProperty(overlayID, "resolution");
|
||||
if (resolution === undefined) {
|
||||
return null;
|
||||
}
|
||||
resolution.z = 1;// Circumvent divide-by-zero.
|
||||
var scale = Overlays.getProperty(overlayID, "dimensions");
|
||||
if (scale === undefined) {
|
||||
return null;
|
||||
}
|
||||
scale.z = 0.01;// overlay dimensions are 2D, not 3D.
|
||||
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
|
||||
} else {
|
||||
dimensions = Overlays.getProperty(overlayID, "dimensions");
|
||||
if (dimensions === undefined) {
|
||||
return null;
|
||||
}
|
||||
if (!dimensions.z) {
|
||||
dimensions.z = 0.01;// sometimes overlay dimensions are 2D, not 3D.
|
||||
}
|
||||
}
|
||||
var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z };
|
||||
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT);
|
||||
|
||||
// 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner.
|
||||
var position2D = {
|
||||
x: normalizedPosition.x * dimensions.x,
|
||||
y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis
|
||||
};
|
||||
|
||||
return {
|
||||
entityID: null,
|
||||
overlayID: overlayID,
|
||||
distance: distance,
|
||||
position: worldPos,
|
||||
position2D: position2D,
|
||||
normal: normal,
|
||||
normalizedPosition: normalizedPosition,
|
||||
dimensions: dimensions,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
function distance2D(a, b) {
|
||||
var dx = (a.x - b.x);
|
||||
var dy = (a.y - b.y);
|
||||
|
@ -277,16 +98,11 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
function OverlayLaserInput(hand) {
|
||||
this.hand = hand;
|
||||
this.active = false;
|
||||
this.previousLaserClikcedTarget = false;
|
||||
this.previousLaserClickedTarget = false;
|
||||
this.laserPressingTarget = false;
|
||||
this.tabletScreenID = null;
|
||||
this.mode = "none";
|
||||
this.laserTargetID = null;
|
||||
this.laserTarget = null;
|
||||
this.pressEnterLaserTarget = null;
|
||||
this.hover = false;
|
||||
this.target = null;
|
||||
this.lastValidTargetID = this.tabletTargetID;
|
||||
|
||||
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
|
@ -307,23 +123,51 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
};
|
||||
|
||||
this.stealTouchFocus = function(laserTarget) {
|
||||
this.requestTouchFocus(laserTarget);
|
||||
this.hasTouchFocus = function(laserTarget) {
|
||||
return (laserTarget.overlayID === this.hoverOverlay);
|
||||
};
|
||||
|
||||
this.requestTouchFocus = function(laserTarget) {
|
||||
if (laserTarget !== null || laserTarget !== undefined) {
|
||||
sendHoverEnterEventToLaserTarget(this.hand, this.laserTarget);
|
||||
this.lastValidTargetID = laserTarget;
|
||||
if (laserTarget.overlayID &&
|
||||
laserTarget.overlayID !== this.hoverOverlay) {
|
||||
this.hoverOverlay = laserTarget.overlayID;
|
||||
TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, laserTarget);
|
||||
}
|
||||
};
|
||||
|
||||
this.relinquishTouchFocus = function() {
|
||||
// send hover leave event.
|
||||
var pointerEvent = { type: "Move", id: this.hand + 1 };
|
||||
Overlays.sendMouseMoveOnOverlay(this.lastValidTargetID, pointerEvent);
|
||||
Overlays.sendHoverOverOverlay(this.lastValidTargetID, pointerEvent);
|
||||
Overlays.sendHoverLeaveOverlay(this.lastValidID, pointerEvent);
|
||||
if (this.hoverOverlay) {
|
||||
var pointerEvent = { type: "Move", id: this.hand + 1 };
|
||||
Overlays.sendMouseMoveOnOverlay(this.hoverOverlay, pointerEvent);
|
||||
Overlays.sendHoverOverOverlay(this.hoverOverlay, pointerEvent);
|
||||
Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent);
|
||||
this.hoverOverlay = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.relinquishStylusTargetTouchFocus = function(laserTarget) {
|
||||
var stylusModuleNames = ["LeftTabletStylusInput", "RightTabletStylusError"];
|
||||
for (var i = 0; i < stylusModuleNames.length; i++) {
|
||||
var stylusModule = getEnabledModuleByName(stylusModuleNames[i]);
|
||||
if (stylusModule) {
|
||||
if (stylusModule.hoverOverlay === laserTarget.overlayID) {
|
||||
stylusModule.relinquishTouchFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.stealTouchFocus = function(laserTarget) {
|
||||
if (laserTarget.overlayID === this.getOtherModule().hoverOverlay) {
|
||||
this.getOtherModule().relinquishTouchFocus();
|
||||
}
|
||||
|
||||
// If the focus target we want to request is the same of one of the stylus
|
||||
// tell the stylus to relinquish it focus on our target
|
||||
this.relinquishStylusTargetTouchFocus(laserTarget);
|
||||
|
||||
this.requestTouchFocus(laserTarget);
|
||||
};
|
||||
|
||||
this.updateLaserPointer = function(controllerData) {
|
||||
|
@ -345,38 +189,23 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.processControllerTriggers = function(controllerData) {
|
||||
if (controllerData.triggerClicks[this.hand]) {
|
||||
this.mode = "full";
|
||||
this.laserPressingTarget = true;
|
||||
this.hover = false;
|
||||
} else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
||||
this.mode = "half";
|
||||
this.laserPressingTarget = false;
|
||||
this.hover = true;
|
||||
this.requestTouchFocus(this.laserTargetID);
|
||||
} else {
|
||||
this.mode = "none";
|
||||
this.laserPressingTarget = false;
|
||||
this.hover = false;
|
||||
this.relinquishTouchFocus();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
this.hovering = function() {
|
||||
if (!laserTargetHasKeyboardFocus(this.laserTagetID)) {
|
||||
setKeyboardFocusOnLaserTarget(this.laserTargetID);
|
||||
}
|
||||
sendHoverOverEventToLaserTarget(this.hand, this.laserTarget);
|
||||
};
|
||||
|
||||
this.laserPressEnter = function () {
|
||||
sendTouchStartEventToLaserTarget(this.hand, this.laserTarget);
|
||||
this.stealTouchFocus(this.laserTarget);
|
||||
TouchEventUtils.sendTouchStartEventToTouchTarget(this.hand, this.laserTarget);
|
||||
Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand);
|
||||
|
||||
this.touchingEnterTimer = 0;
|
||||
this.pressEnterLaserTarget = this.laserTarget;
|
||||
this.deadspotExpired = false;
|
||||
|
||||
var LASER_PRESS_TO_MOVE_DEADSPOT = 0.026;
|
||||
var LASER_PRESS_TO_MOVE_DEADSPOT = 0.094;
|
||||
this.deadspotRadius = Math.tan(LASER_PRESS_TO_MOVE_DEADSPOT) * this.laserTarget.distance;
|
||||
};
|
||||
|
||||
|
@ -386,15 +215,15 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
}
|
||||
|
||||
// special case to handle home button.
|
||||
if (this.laserTargetID === HMD.homeButtonID) {
|
||||
Messages.sendLocalMessage("home", this.laserTargetID);
|
||||
if (this.laserTarget.overlayID === HMD.homeButtonID) {
|
||||
Messages.sendLocalMessage("home", this.laserTarget.overlayID);
|
||||
}
|
||||
|
||||
// send press event
|
||||
if (this.deadspotExpired) {
|
||||
sendTouchEndEventToLaserTarget(this.hand, this.laserTarget);
|
||||
TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.laserTarget);
|
||||
} else {
|
||||
sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget);
|
||||
TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.pressEnterLaserTarget);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -402,41 +231,84 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.touchingEnterTimer += dt;
|
||||
|
||||
if (this.laserTarget) {
|
||||
var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds
|
||||
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
|
||||
distance2D( this.laserTarget.position2D,
|
||||
this.pressEnterLaserTarget.position2D) > this.deadspotRadius) {
|
||||
sendTouchMoveEventToLaserTarget(this.hand, this.laserTarget);
|
||||
this.deadspotExpired = true;
|
||||
if (controllerData.triggerClicks[this.hand]) {
|
||||
var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds
|
||||
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
|
||||
distance2D(this.laserTarget.position2D,
|
||||
this.pressEnterLaserTarget.position2D) > this.deadspotRadius) {
|
||||
TouchEventUtils.sendTouchMoveEventToTouchTarget(this.hand, this.laserTarget);
|
||||
this.deadspotExpired = true;
|
||||
}
|
||||
} else {
|
||||
this.laserPressingTarget = false;
|
||||
}
|
||||
} else {
|
||||
this.laserPressingTarget = false;
|
||||
}
|
||||
};
|
||||
|
||||
this.releaseTouchEvent = function() {
|
||||
sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget);
|
||||
this.processLaser = function(controllerData) {
|
||||
if (this.shouldExit(controllerData) || this.getOtherModule().active) {
|
||||
this.exitModule();
|
||||
return false;
|
||||
}
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
var laserTarget = TouchEventUtils.composeTouchTargetFromIntersection(intersection);
|
||||
|
||||
if (controllerData.triggerClicks[this.hand]) {
|
||||
this.laserTarget = laserTarget;
|
||||
this.laserPressingTarget = true;
|
||||
} else {
|
||||
this.requestTouchFocus(laserTarget);
|
||||
|
||||
if (!TouchEventUtils.touchTargetHasKeyboardFocus(laserTarget)) {
|
||||
TouchEventUtils.setKeyboardFocusOnTouchTarget(laserTarget);
|
||||
}
|
||||
|
||||
if (this.hasTouchFocus(laserTarget) && !this.laserPressingTarget) {
|
||||
TouchEventUtils.sendHoverOverEventToTouchTarget(this.hand, laserTarget);
|
||||
}
|
||||
}
|
||||
|
||||
this.processControllerTriggers(controllerData);
|
||||
this.updateLaserPointer(controllerData);
|
||||
this.active = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
this.updateLaserTargets = function(controllerData) {
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
this.laserTargetID = intersection.objectID;
|
||||
this.laserTarget = calculateLaserTargetFromOverlay(intersection.intersection, intersection.objectID);
|
||||
this.grabModuleWantsNearbyOverlay = function(controllerData) {
|
||||
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
||||
var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay";
|
||||
var nearGrabModule = getEnabledModuleByName(nearGrabName);
|
||||
if (nearGrabModule) {
|
||||
var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand];
|
||||
var grabbableOverlays = candidateOverlays.filter(function(overlayID) {
|
||||
return Overlays.getProperty(overlayID, "grabbable");
|
||||
});
|
||||
var target = nearGrabModule.getTargetID(grabbableOverlays, controllerData);
|
||||
if (target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
this.shouldExit = function(controllerData) {
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay";
|
||||
var nearGrabModule = getEnabledModuleByName(nearGrabName);
|
||||
var status = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []);
|
||||
var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY);
|
||||
var triggerOff = (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE);
|
||||
return offOverlay || status.active || triggerOff;
|
||||
var grabbingOverlay = this.grabModuleWantsNearbyOverlay(controllerData);
|
||||
return offOverlay || grabbingOverlay || triggerOff;
|
||||
};
|
||||
|
||||
this.exitModule = function() {
|
||||
this.releaseTouchEvent();
|
||||
if (this.laserPressingTarget) {
|
||||
this.deadspotExpired = true;
|
||||
this.laserPressExit();
|
||||
this.laserPressingTarget = false;
|
||||
}
|
||||
this.deleteContextOverlay();
|
||||
this.relinquishTouchFocus();
|
||||
this.reset();
|
||||
this.updateLaserPointer();
|
||||
|
@ -444,12 +316,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
};
|
||||
|
||||
this.reset = function() {
|
||||
this.hover = false;
|
||||
this.pressEnterLaserTarget = null;
|
||||
this.laserTarget = null;
|
||||
this.laserTargetID = null;
|
||||
this.laserPressingTarget = false;
|
||||
this.previousLaserClickedTarget = null;
|
||||
this.mode = "none";
|
||||
this.active = false;
|
||||
};
|
||||
|
@ -467,35 +333,13 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
};
|
||||
|
||||
this.isReady = function (controllerData) {
|
||||
this.target = null;
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
if (intersection.type === RayPick.INTERSECTED_OVERLAY) {
|
||||
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE && !this.getOtherModule().active) {
|
||||
this.target = intersection.objectID;
|
||||
this.active = true;
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
this.deleteContextOverlay();
|
||||
}
|
||||
if (this.processLaser(controllerData)) {
|
||||
return makeRunningValues(true, [], []);
|
||||
}
|
||||
this.reset();
|
||||
return makeRunningValues(false, [], []);
|
||||
};
|
||||
|
||||
this.run = function (controllerData, deltaTime) {
|
||||
if (this.shouldExit(controllerData)) {
|
||||
this.exitModule();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) {
|
||||
this.deleteContextOverlay();
|
||||
}
|
||||
|
||||
this.updateLaserTargets(controllerData);
|
||||
this.processControllerTriggers(controllerData);
|
||||
this.updateLaserPointer(controllerData);
|
||||
|
||||
if (!this.previousLaserClickedTarget && this.laserPressingTarget) {
|
||||
this.laserPressEnter();
|
||||
}
|
||||
|
@ -508,11 +352,11 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.laserPressing(controllerData, deltaTime);
|
||||
}
|
||||
|
||||
if (this.hover) {
|
||||
this.hovering();
|
||||
if (this.processLaser(controllerData)) {
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
return makeRunningValues(true, [], []);
|
||||
};
|
||||
|
||||
this.cleanup = function () {
|
||||
|
|
|
@ -16,238 +16,14 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
(function() {
|
||||
|
||||
var TouchEventUtils = Script.require("/~/system/libraries/touchEventUtils.js");
|
||||
// triggered when stylus presses a web overlay/entity
|
||||
var HAPTIC_STYLUS_STRENGTH = 1.0;
|
||||
var HAPTIC_STYLUS_DURATION = 20.0;
|
||||
|
||||
var WEB_DISPLAY_STYLUS_DISTANCE = 0.5;
|
||||
var WEB_STYLUS_LENGTH = 0.2;
|
||||
var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand
|
||||
|
||||
|
||||
function stylusTargetHasKeyboardFocus(stylusTarget) {
|
||||
if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) {
|
||||
return Entities.keyboardFocusEntity === stylusTarget.entityID;
|
||||
} else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) {
|
||||
return Overlays.keyboardFocusOverlay === stylusTarget.overlayID;
|
||||
}
|
||||
}
|
||||
|
||||
function setKeyboardFocusOnStylusTarget(stylusTarget) {
|
||||
if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID &&
|
||||
Entities.wantsHandControllerPointerEvents(stylusTarget.entityID)) {
|
||||
Overlays.keyboardFocusOverlay = NULL_UUID;
|
||||
Entities.keyboardFocusEntity = stylusTarget.entityID;
|
||||
} else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.keyboardFocusOverlay = stylusTarget.overlayID;
|
||||
Entities.keyboardFocusEntity = NULL_UUID;
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverEnterEventToStylusTarget(hand, stylusTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: stylusTarget.position2D,
|
||||
pos3D: stylusTarget.position,
|
||||
normal: stylusTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) {
|
||||
Entities.sendHoverEnterEntity(stylusTarget.entityID, pointerEvent);
|
||||
} else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendHoverEnterOverlay(stylusTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverOverEventToStylusTarget(hand, stylusTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: stylusTarget.position2D,
|
||||
pos3D: stylusTarget.position,
|
||||
normal: stylusTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) {
|
||||
Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent);
|
||||
Entities.sendHoverOverEntity(stylusTarget.entityID, pointerEvent);
|
||||
} else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent);
|
||||
Overlays.sendHoverOverOverlay(stylusTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchStartEventToStylusTarget(hand, stylusTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Press",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: stylusTarget.position2D,
|
||||
pos3D: stylusTarget.position,
|
||||
normal: stylusTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) {
|
||||
Entities.sendMousePressOnEntity(stylusTarget.entityID, pointerEvent);
|
||||
Entities.sendClickDownOnEntity(stylusTarget.entityID, pointerEvent);
|
||||
} else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMousePressOnOverlay(stylusTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchEndEventToStylusTarget(hand, stylusTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Release",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: stylusTarget.position2D,
|
||||
pos3D: stylusTarget.position,
|
||||
normal: stylusTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal),
|
||||
button: "Primary"
|
||||
};
|
||||
|
||||
if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) {
|
||||
Entities.sendMouseReleaseOnEntity(stylusTarget.entityID, pointerEvent);
|
||||
Entities.sendClickReleaseOnEntity(stylusTarget.entityID, pointerEvent);
|
||||
Entities.sendHoverLeaveEntity(stylusTarget.entityID, pointerEvent);
|
||||
} else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMouseReleaseOnOverlay(stylusTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchMoveEventToStylusTarget(hand, stylusTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: stylusTarget.position2D,
|
||||
pos3D: stylusTarget.position,
|
||||
normal: stylusTarget.normal,
|
||||
direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) {
|
||||
Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent);
|
||||
Entities.sendHoldingClickOnEntity(stylusTarget.entityID, pointerEvent);
|
||||
} else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) {
|
||||
Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// will return undefined if overlayID does not exist.
|
||||
function calculateStylusTargetFromOverlay(stylusTip, overlayID) {
|
||||
var overlayPosition = Overlays.getProperty(overlayID, "position");
|
||||
if (overlayPosition === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// project stylusTip onto overlay plane.
|
||||
var overlayRotation = Overlays.getProperty(overlayID, "rotation");
|
||||
if (overlayRotation === undefined) {
|
||||
return;
|
||||
}
|
||||
var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1});
|
||||
var distance = Vec3.dot(Vec3.subtract(stylusTip.position, overlayPosition), normal);
|
||||
var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance));
|
||||
|
||||
// calclulate normalized position
|
||||
var invRot = Quat.inverse(overlayRotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition));
|
||||
var dpi = Overlays.getProperty(overlayID, "dpi");
|
||||
|
||||
var dimensions;
|
||||
if (dpi) {
|
||||
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property
|
||||
// is used as a scale.
|
||||
var resolution = Overlays.getProperty(overlayID, "resolution");
|
||||
if (resolution === undefined) {
|
||||
return;
|
||||
}
|
||||
resolution.z = 1; // Circumvent divide-by-zero.
|
||||
var scale = Overlays.getProperty(overlayID, "dimensions");
|
||||
if (scale === undefined) {
|
||||
return;
|
||||
}
|
||||
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
|
||||
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
|
||||
} else {
|
||||
dimensions = Overlays.getProperty(overlayID, "dimensions");
|
||||
if (dimensions === undefined) {
|
||||
return;
|
||||
}
|
||||
if (!dimensions.z) {
|
||||
dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D.
|
||||
}
|
||||
}
|
||||
var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z };
|
||||
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT);
|
||||
|
||||
// 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner.
|
||||
var position2D = {
|
||||
x: normalizedPosition.x * dimensions.x,
|
||||
y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis
|
||||
};
|
||||
|
||||
return {
|
||||
entityID: null,
|
||||
overlayID: overlayID,
|
||||
distance: distance,
|
||||
position: position,
|
||||
position2D: position2D,
|
||||
normal: normal,
|
||||
normalizedPosition: normalizedPosition,
|
||||
dimensions: dimensions,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
// will return undefined if entity does not exist.
|
||||
function calculateStylusTargetFromEntity(stylusTip, props) {
|
||||
if (props.rotation === undefined) {
|
||||
// if rotation is missing from props object, then this entity has probably been deleted.
|
||||
return;
|
||||
}
|
||||
|
||||
// project stylus tip onto entity plane.
|
||||
var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1});
|
||||
Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0});
|
||||
var distance = Vec3.dot(Vec3.subtract(stylusTip.position, props.position), normal);
|
||||
var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance));
|
||||
|
||||
// generate normalized coordinates
|
||||
var invRot = Quat.inverse(props.rotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position));
|
||||
var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z };
|
||||
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint);
|
||||
|
||||
// 2D position on entity plane in meters, relative to the bounding box upper-left hand corner.
|
||||
var position2D = {
|
||||
x: normalizedPosition.x * props.dimensions.x,
|
||||
y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis
|
||||
};
|
||||
|
||||
return {
|
||||
entityID: props.id,
|
||||
entityProps: props,
|
||||
overlayID: null,
|
||||
distance: distance,
|
||||
position: position,
|
||||
position2D: position2D,
|
||||
normal: normal,
|
||||
normalizedPosition: normalizedPosition,
|
||||
dimensions: props.dimensions,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
var WEB_TOUCH_Y_OFFSET = 0.105; // how far forward (or back with a negative number) to slide stylus in hand
|
||||
|
||||
function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) {
|
||||
for (var i = 0; i < stylusTargets.length; i++) {
|
||||
|
@ -330,7 +106,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
100);
|
||||
|
||||
this.getOtherHandController = function() {
|
||||
return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand;
|
||||
return (this.hand === RIGHT_HAND) ? leftTabletStylusInput : rightTabletStylusInput;
|
||||
};
|
||||
|
||||
this.handToController = function() {
|
||||
|
@ -430,12 +206,12 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
stylusTarget.entityID !== this.hoverEntity &&
|
||||
stylusTarget.entityID !== this.getOtherHandController().hoverEntity) {
|
||||
this.hoverEntity = stylusTarget.entityID;
|
||||
sendHoverEnterEventToStylusTarget(this.hand, stylusTarget);
|
||||
TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget);
|
||||
} else if (stylusTarget.overlayID &&
|
||||
stylusTarget.overlayID !== this.hoverOverlay &&
|
||||
stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) {
|
||||
this.hoverOverlay = stylusTarget.overlayID;
|
||||
sendHoverEnterEventToStylusTarget(this.hand, stylusTarget);
|
||||
TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -492,7 +268,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
for (i = 0; i < candidateEntities.length; i++) {
|
||||
props = candidateEntities[i];
|
||||
if (props && props.type === "Web") {
|
||||
stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, candidateEntities[i]);
|
||||
stylusTarget = TouchEventUtils.calculateTouchTargetFromEntity(this.stylusTip, candidateEntities[i]);
|
||||
if (stylusTarget) {
|
||||
stylusTargets.push(stylusTarget);
|
||||
}
|
||||
|
@ -502,7 +278,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
// add the tabletScreen, if it is valid
|
||||
if (HMD.tabletScreenID && HMD.tabletScreenID !== NULL_UUID &&
|
||||
Overlays.getProperty(HMD.tabletScreenID, "visible")) {
|
||||
stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.tabletScreenID);
|
||||
stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, HMD.tabletScreenID);
|
||||
if (stylusTarget) {
|
||||
stylusTargets.push(stylusTarget);
|
||||
}
|
||||
|
@ -511,7 +287,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
// add the tablet home button.
|
||||
if (HMD.homeButtonID && HMD.homeButtonID !== NULL_UUID &&
|
||||
Overlays.getProperty(HMD.homeButtonID, "visible")) {
|
||||
stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.homeButtonID);
|
||||
stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, HMD.homeButtonID);
|
||||
if (stylusTarget) {
|
||||
stylusTargets.push(stylusTarget);
|
||||
}
|
||||
|
@ -530,9 +306,9 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
|
||||
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
|
||||
this.isNearStylusTarget = isNearStylusTarget(stylusTargets,
|
||||
(EDGE_BORDER + hysteresisOffset) * sensorScaleFactor,
|
||||
(TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor,
|
||||
(WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor);
|
||||
(EDGE_BORDER + hysteresisOffset) * sensorScaleFactor,
|
||||
(TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor,
|
||||
(WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor);
|
||||
|
||||
if (this.isNearStylusTarget) {
|
||||
if (!this.useFingerInsteadOfStylus) {
|
||||
|
@ -556,12 +332,12 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
|
||||
this.requestTouchFocus(nearestStylusTarget);
|
||||
|
||||
if (!stylusTargetHasKeyboardFocus(nearestStylusTarget)) {
|
||||
setKeyboardFocusOnStylusTarget(nearestStylusTarget);
|
||||
if (!TouchEventUtils.touchTargetHasKeyboardFocus(nearestStylusTarget)) {
|
||||
TouchEventUtils.setKeyboardFocusOnTouchTarget(nearestStylusTarget);
|
||||
}
|
||||
|
||||
if (this.hasTouchFocus(nearestStylusTarget)) {
|
||||
sendHoverOverEventToStylusTarget(this.hand, nearestStylusTarget);
|
||||
if (this.hasTouchFocus(nearestStylusTarget) && !this.stylusTouchingTarget) {
|
||||
TouchEventUtils.sendHoverOverEventToTouchTarget(this.hand, nearestStylusTarget);
|
||||
}
|
||||
|
||||
// filter out presses when tip is moving away from tablet.
|
||||
|
@ -592,14 +368,14 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
|
||||
this.stylusTouchingEnter = function () {
|
||||
this.stealTouchFocus(this.stylusTarget);
|
||||
sendTouchStartEventToStylusTarget(this.hand, this.stylusTarget);
|
||||
TouchEventUtils.sendTouchStartEventToTouchTarget(this.hand, this.stylusTarget);
|
||||
Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand);
|
||||
|
||||
this.touchingEnterTimer = 0;
|
||||
this.touchingEnterStylusTarget = this.stylusTarget;
|
||||
this.deadspotExpired = false;
|
||||
|
||||
var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381;
|
||||
var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481;
|
||||
this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT;
|
||||
};
|
||||
|
||||
|
@ -616,9 +392,9 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
|
||||
// send press event
|
||||
if (this.deadspotExpired) {
|
||||
sendTouchEndEventToStylusTarget(this.hand, this.stylusTarget);
|
||||
TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.stylusTarget);
|
||||
} else {
|
||||
sendTouchEndEventToStylusTarget(this.hand, this.touchingEnterStylusTarget);
|
||||
TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.touchingEnterStylusTarget);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -627,9 +403,9 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.touchingEnterTimer += dt;
|
||||
|
||||
if (this.stylusTarget.entityID) {
|
||||
this.stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps);
|
||||
this.stylusTarget = TouchEventUtils.calculateTouchTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps);
|
||||
} else if (this.stylusTarget.overlayID) {
|
||||
this.stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID);
|
||||
this.stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID);
|
||||
}
|
||||
|
||||
var TABLET_MIN_TOUCH_DISTANCE = -0.1;
|
||||
|
@ -642,7 +418,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
|
||||
distance2D(this.stylusTarget.position2D,
|
||||
this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) {
|
||||
sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget);
|
||||
TouchEventUtils.sendTouchMoveEventToTouchTarget(this.hand, this.stylusTarget);
|
||||
this.deadspotExpired = true;
|
||||
}
|
||||
} else {
|
||||
|
@ -654,12 +430,11 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
};
|
||||
|
||||
this.overlayLaserActive = function(controllerData) {
|
||||
var overlayLaserModule =
|
||||
getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput");
|
||||
if (overlayLaserModule) {
|
||||
return overlayLaserModule.isReady(controllerData).active;
|
||||
}
|
||||
return false;
|
||||
var rightOverlayLaserModule = getEnabledModuleByName("RightOverlayLaserInput");
|
||||
var leftOverlayLaserModule = getEnabledModuleByName("LeftOverlayLaserInput");
|
||||
var rightModuleRunning = rightOverlayLaserModule ? !rightOverlayLaserModule.shouldExit(controllerData) : false;
|
||||
var leftModuleRunning = leftOverlayLaserModule ? !leftOverlayLaserModule.shouldExit(controllerData) : false;
|
||||
return leftModuleRunning || rightModuleRunning;
|
||||
};
|
||||
|
||||
this.isReady = function (controllerData) {
|
||||
|
|
|
@ -49,9 +49,9 @@ function calcSpawnInfo(hand, landscape) {
|
|||
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
|
||||
|
||||
var forward = Quat.getForward(headRot);
|
||||
var FORWARD_OFFSET = 0.6 * MyAvatar.sensorToWorldScale;
|
||||
var FORWARD_OFFSET = 0.5 * MyAvatar.sensorToWorldScale;
|
||||
finalPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward));
|
||||
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0});
|
||||
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
|
||||
return {
|
||||
position: finalPosition,
|
||||
rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180)
|
||||
|
|
|
@ -318,6 +318,8 @@ if (typeof module !== 'undefined') {
|
|||
makeRunningValues: makeRunningValues,
|
||||
LEFT_HAND: LEFT_HAND,
|
||||
RIGHT_HAND: RIGHT_HAND,
|
||||
BUMPER_ON_VALUE: BUMPER_ON_VALUE
|
||||
BUMPER_ON_VALUE: BUMPER_ON_VALUE,
|
||||
projectOntoOverlayXYPlane: projectOntoOverlayXYPlane,
|
||||
projectOntoEntityXYPlane: projectOntoEntityXYPlane
|
||||
};
|
||||
}
|
||||
|
|
270
scripts/system/libraries/touchEventUtils.js
Normal file
270
scripts/system/libraries/touchEventUtils.js
Normal file
|
@ -0,0 +1,270 @@
|
|||
"use strict";
|
||||
|
||||
// touchEventUtils.js
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
|
||||
controllerDispatcher.NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues,
|
||||
Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, controllerDispatcher.ZERO_VEC,
|
||||
AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset
|
||||
*/
|
||||
|
||||
var controllerDispatcher = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
function touchTargetHasKeyboardFocus(touchTarget) {
|
||||
if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) {
|
||||
return Entities.keyboardFocusEntity === touchTarget.entityID;
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) {
|
||||
return Overlays.keyboardFocusOverlay === touchTarget.overlayID;
|
||||
}
|
||||
}
|
||||
|
||||
function setKeyboardFocusOnTouchTarget(touchTarget) {
|
||||
if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID &&
|
||||
Entities.wantsHandControllerPointerEvents(touchTarget.entityID)) {
|
||||
Overlays.keyboardFocusOverlay = controllerDispatcher.NULL_UUID;
|
||||
Entities.keyboardFocusEntity = touchTarget.entityID;
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) {
|
||||
Overlays.keyboardFocusOverlay = touchTarget.overlayID;
|
||||
Entities.keyboardFocusEntity = controllerDispatcher.NULL_UUID;
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverEnterEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) {
|
||||
Entities.sendHoverEnterEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) {
|
||||
Overlays.sendHoverEnterOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverOverEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) {
|
||||
Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendHoverOverEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) {
|
||||
Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
Overlays.sendHoverOverOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchStartEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Press",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) {
|
||||
Entities.sendMousePressOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendClickDownOnEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) {
|
||||
Overlays.sendMousePressOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchEndEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Release",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "Primary"
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) {
|
||||
Entities.sendMouseReleaseOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendClickReleaseOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendHoverLeaveEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) {
|
||||
Overlays.sendMouseReleaseOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchMoveEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) {
|
||||
Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendHoldingClickOnEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) {
|
||||
Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function composeTouchTargetFromIntersection(intersection) {
|
||||
var isEntity = (intersection.type === RayPick.INTERSECTED_ENTITY);
|
||||
var objectID = intersection.objectID;
|
||||
var worldPos = intersection.intersection;
|
||||
var props = null;
|
||||
if (isEntity) {
|
||||
props = Entities.getProperties(intersection.objectID);
|
||||
}
|
||||
|
||||
var position2D =(isEntity ? controllerDispatcher.projectOntoEntityXYPlane(objectID, worldPos, props) :
|
||||
controllerDispatcher.projectOntoOverlayXYPlane(objectID, worldPos));
|
||||
return {
|
||||
entityID: isEntity ? objectID : null,
|
||||
overlayID: isEntity ? null : objectID,
|
||||
distance: intersection.distance,
|
||||
position: worldPos,
|
||||
position2D: position2D,
|
||||
normal: intersection.surfaceNormal
|
||||
};
|
||||
}
|
||||
|
||||
// will return undefined if overlayID does not exist.
|
||||
function calculateTouchTargetFromOverlay(touchTip, overlayID) {
|
||||
var overlayPosition = Overlays.getProperty(overlayID, "position");
|
||||
if (overlayPosition === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// project touchTip onto overlay plane.
|
||||
var overlayRotation = Overlays.getProperty(overlayID, "rotation");
|
||||
if (overlayRotation === undefined) {
|
||||
return;
|
||||
}
|
||||
var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1});
|
||||
var distance = Vec3.dot(Vec3.subtract(touchTip.position, overlayPosition), normal);
|
||||
var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance));
|
||||
|
||||
// calclulate normalized position
|
||||
var invRot = Quat.inverse(overlayRotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition));
|
||||
var dpi = Overlays.getProperty(overlayID, "dpi");
|
||||
|
||||
var dimensions;
|
||||
if (dpi) {
|
||||
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property
|
||||
// is used as a scale.
|
||||
var resolution = Overlays.getProperty(overlayID, "resolution");
|
||||
if (resolution === undefined) {
|
||||
return;
|
||||
}
|
||||
resolution.z = 1; // Circumvent divide-by-zero.
|
||||
var scale = Overlays.getProperty(overlayID, "dimensions");
|
||||
if (scale === undefined) {
|
||||
return;
|
||||
}
|
||||
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
|
||||
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
|
||||
} else {
|
||||
dimensions = Overlays.getProperty(overlayID, "dimensions");
|
||||
if (dimensions === undefined) {
|
||||
return;
|
||||
}
|
||||
if (!dimensions.z) {
|
||||
dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D.
|
||||
}
|
||||
}
|
||||
var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z };
|
||||
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT);
|
||||
|
||||
// 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner.
|
||||
var position2D = {
|
||||
x: normalizedPosition.x * dimensions.x,
|
||||
y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis
|
||||
};
|
||||
|
||||
return {
|
||||
entityID: null,
|
||||
overlayID: overlayID,
|
||||
distance: distance,
|
||||
position: position,
|
||||
position2D: position2D,
|
||||
normal: normal,
|
||||
normalizedPosition: normalizedPosition,
|
||||
dimensions: dimensions,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
// will return undefined if entity does not exist.
|
||||
function calculateTouchTargetFromEntity(touchTip, props) {
|
||||
if (props.rotation === undefined) {
|
||||
// if rotation is missing from props object, then this entity has probably been deleted.
|
||||
return;
|
||||
}
|
||||
|
||||
// project touch tip onto entity plane.
|
||||
var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1});
|
||||
Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0});
|
||||
var distance = Vec3.dot(Vec3.subtract(touchTip.position, props.position), normal);
|
||||
var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance));
|
||||
|
||||
// generate normalized coordinates
|
||||
var invRot = Quat.inverse(props.rotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position));
|
||||
var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z };
|
||||
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint);
|
||||
|
||||
// 2D position on entity plane in meters, relative to the bounding box upper-left hand corner.
|
||||
var position2D = {
|
||||
x: normalizedPosition.x * props.dimensions.x,
|
||||
y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis
|
||||
};
|
||||
|
||||
return {
|
||||
entityID: props.id,
|
||||
entityProps: props,
|
||||
overlayID: null,
|
||||
distance: distance,
|
||||
position: position,
|
||||
position2D: position2D,
|
||||
normal: normal,
|
||||
normalizedPosition: normalizedPosition,
|
||||
dimensions: props.dimensions,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
calculateTouchTargetFromEntity: calculateTouchTargetFromEntity,
|
||||
calculateTouchTargetFromOverlay: calculateTouchTargetFromOverlay,
|
||||
touchTargetHasKeyboardFocus: touchTargetHasKeyboardFocus,
|
||||
setKeyboardFocusOnTouchTarget: setKeyboardFocusOnTouchTarget,
|
||||
sendHoverEnterEventToTouchTarget: sendHoverEnterEventToTouchTarget,
|
||||
sendHoverOverEventToTouchTarget: sendHoverOverEventToTouchTarget,
|
||||
sendTouchStartEventToTouchTarget: sendTouchStartEventToTouchTarget,
|
||||
sendTouchEndEventToTouchTarget: sendTouchEndEventToTouchTarget,
|
||||
sendTouchMoveEventToTouchTarget: sendTouchMoveEventToTouchTarget,
|
||||
composeTouchTargetFromIntersection: composeTouchTargetFromIntersection
|
||||
};
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
var request = Script.require('request').request;
|
||||
|
||||
var WANT_DEBUG = Settings.getValue('MAKE_USER_CONNECTION_DEBUG', false);
|
||||
var LABEL = "makeUserConnection";
|
||||
var MAX_AVATAR_DISTANCE = 0.2; // m
|
||||
var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed
|
||||
|
@ -120,6 +121,9 @@
|
|||
var successfulHandshakeSound;
|
||||
|
||||
function debug() {
|
||||
if (!WANT_DEBUG) {
|
||||
return;
|
||||
}
|
||||
var stateString = "<" + STATE_STRINGS[state] + ">";
|
||||
var connecting = "[" + connectingId + "/" + connectingHandJointIndex + "]";
|
||||
var current = "[" + currentHand + "/" + currentHandJointIndex + "]"
|
||||
|
@ -372,7 +376,7 @@
|
|||
var myHeadIndex = MyAvatar.getJointIndex("Head");
|
||||
var otherHeadIndex = avatar.getJointIndex("Head");
|
||||
var diff = (avatar.getJointPosition(otherHeadIndex).y - MyAvatar.getJointPosition(myHeadIndex).y) / 2;
|
||||
print("head height difference: " + diff);
|
||||
debug("head height difference: " + diff);
|
||||
updateAnimationData(diff);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -571,7 +571,7 @@
|
|||
|
||||
function onTabletScreenChanged(type, url) {
|
||||
// Opened/closed dialog in tablet or window.
|
||||
var RECORD_URL = "/scripts/system/html/record.html";
|
||||
var RECORD_URL = "/html/record.html";
|
||||
|
||||
if (type === "Web" && url.slice(-RECORD_URL.length) === RECORD_URL) {
|
||||
if (Dialog.finishOnOpen()) {
|
||||
|
|
Loading…
Reference in a new issue