mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 07:23:39 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into spectator-camera
This commit is contained in:
commit
dbb48cd1aa
42 changed files with 1228 additions and 615 deletions
|
@ -17,8 +17,8 @@
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
#include <SharedUtil.h>
|
|
||||||
#include <HifiConfigVariantMap.h>
|
#include <HifiConfigVariantMap.h>
|
||||||
|
#include <SharedUtil.h>
|
||||||
#include <ShutdownEventListener.h>
|
#include <ShutdownEventListener.h>
|
||||||
|
|
||||||
#include "Assignment.h"
|
#include "Assignment.h"
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
{
|
{
|
||||||
"label": "Places / Paths",
|
"label": "Places / Paths",
|
||||||
"html_id": "places_paths",
|
"html_id": "places_paths",
|
||||||
|
"restart": false,
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "paths",
|
"name": "paths",
|
||||||
|
@ -75,6 +76,7 @@
|
||||||
{
|
{
|
||||||
"name": "descriptors",
|
"name": "descriptors",
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
|
"restart": false,
|
||||||
"help": "This data will be queryable from your server. It may be collected by High Fidelity and used to share your domain with others.",
|
"help": "This data will be queryable from your server. It may be collected by High Fidelity and used to share your domain with others.",
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,10 +34,24 @@ $(document).ready(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#nodes-table tbody').html(nodeTemplate(json));
|
$('#nodes-table tbody').html(nodeTemplate(json));
|
||||||
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
|
// we assume a 401 means the DS has restarted
|
||||||
|
// and no longer has our OAuth produced uuid
|
||||||
|
// so just reload and re-auth
|
||||||
|
if (jqXHR.status == 401) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$.getJSON("assignments.json", function(json){
|
$.getJSON("assignments.json", function(json){
|
||||||
$('#assignments-table tbody').html(queuedTemplate(json));
|
$('#assignments-table tbody').html(queuedTemplate(json));
|
||||||
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
|
// we assume a 401 means the DS has restarted
|
||||||
|
// and no longer has our OAuth produced uuid
|
||||||
|
// so just reload and re-auth
|
||||||
|
if (jqXHR.status == 401) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,11 @@
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
#include <Trace.h>
|
||||||
|
#include <StatTracker.h>
|
||||||
|
|
||||||
#include "DomainServerNodeData.h"
|
#include "DomainServerNodeData.h"
|
||||||
#include "NodeConnectionData.h"
|
#include "NodeConnectionData.h"
|
||||||
#include <Trace.h>
|
|
||||||
#include <StatTracker.h>
|
|
||||||
|
|
||||||
int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
||||||
|
|
||||||
|
@ -162,8 +162,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
|
|
||||||
_gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
|
_gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
|
||||||
|
|
||||||
|
//send signal to DomainMetadata when descriptors changed
|
||||||
_metadata = new DomainMetadata(this);
|
_metadata = new DomainMetadata(this);
|
||||||
|
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
|
||||||
|
_metadata, &DomainMetadata::descriptorsChanged);
|
||||||
|
|
||||||
qDebug() << "domain-server is running";
|
qDebug() << "domain-server is running";
|
||||||
static const QString AC_SUBNET_WHITELIST_SETTING_PATH = "security.ac_subnet_whitelist";
|
static const QString AC_SUBNET_WHITELIST_SETTING_PATH = "security.ac_subnet_whitelist";
|
||||||
|
@ -1972,7 +1974,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
return _settingsManager.handleAuthenticatedHTTPRequest(connection, url);
|
return _settingsManager.handleAuthenticatedHTTPRequest(connection, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID";
|
static const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID";
|
||||||
|
static const QString STATE_QUERY_KEY = "state";
|
||||||
|
|
||||||
bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url, bool skipSubHandler) {
|
bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url, bool skipSubHandler) {
|
||||||
qDebug() << "HTTPS request received at" << url.toString();
|
qDebug() << "HTTPS request received at" << url.toString();
|
||||||
|
@ -1983,10 +1986,9 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
||||||
const QString CODE_QUERY_KEY = "code";
|
const QString CODE_QUERY_KEY = "code";
|
||||||
QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY);
|
QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY);
|
||||||
|
|
||||||
const QString STATE_QUERY_KEY = "state";
|
|
||||||
QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY));
|
QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY));
|
||||||
|
|
||||||
if (!authorizationCode.isEmpty() && !stateUUID.isNull()) {
|
if (!authorizationCode.isEmpty() && !stateUUID.isNull() && _webAuthenticationStateSet.remove(stateUUID)) {
|
||||||
// fire off a request with this code and state to get an access token for the user
|
// fire off a request with this code and state to get an access token for the user
|
||||||
|
|
||||||
const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token";
|
const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token";
|
||||||
|
@ -2004,47 +2006,83 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
||||||
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit());
|
QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit());
|
||||||
|
connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::tokenGrantFinished);
|
||||||
|
|
||||||
if (_webAuthenticationStateSet.remove(stateUUID)) {
|
// add this connection to our list of pending connections so that we can hold the response
|
||||||
// this is a web user who wants to auth to access web interface
|
_pendingOAuthConnections.insert(stateUUID, connection);
|
||||||
// we hold the response back to them until we get their profile information
|
|
||||||
// and can decide if they are let in or not
|
|
||||||
|
|
||||||
QEventLoop loop;
|
// set the state UUID on the reply so that we can associate the response with the connection later
|
||||||
connect(tokenReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
tokenReply->setProperty(STATE_QUERY_KEY.toLocal8Bit(), stateUUID);
|
||||||
|
|
||||||
// start the loop for the token request
|
return true;
|
||||||
loop.exec();
|
} else {
|
||||||
|
connection->respond(HTTPConnection::StatusCode400);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) {
|
||||||
|
// grab the UUID state property from the reply
|
||||||
|
QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid();
|
||||||
|
|
||||||
|
if (!stateUUID.isNull()) {
|
||||||
|
return _pendingOAuthConnections.take(stateUUID);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::tokenGrantFinished() {
|
||||||
|
auto tokenReply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
|
if (tokenReply) {
|
||||||
|
if (tokenReply->error() == QNetworkReply::NoError) {
|
||||||
|
// now that we have a token for this profile, send off a profile request
|
||||||
QNetworkReply* profileReply = profileRequestGivenTokenReply(tokenReply);
|
QNetworkReply* profileReply = profileRequestGivenTokenReply(tokenReply);
|
||||||
|
|
||||||
// stop the loop once the profileReply is complete
|
// forward along the state UUID that we kept with the token request
|
||||||
connect(profileReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
profileReply->setProperty(STATE_QUERY_KEY.toLocal8Bit(), tokenReply->property(STATE_QUERY_KEY.toLocal8Bit()));
|
||||||
|
|
||||||
// restart the loop for the profile request
|
connect(profileReply, &QNetworkReply::finished, this, &DomainServer::profileRequestFinished);
|
||||||
loop.exec();
|
} else {
|
||||||
|
// the token grant failed, send back a 500 (assuming the connection is still around)
|
||||||
|
auto connection = connectionFromReplyWithState(tokenReply);
|
||||||
|
|
||||||
|
if (connection) {
|
||||||
|
connection->respond(HTTPConnection::StatusCode500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenReply->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::profileRequestFinished() {
|
||||||
|
|
||||||
|
auto profileReply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
|
if (profileReply) {
|
||||||
|
auto connection = connectionFromReplyWithState(profileReply);
|
||||||
|
|
||||||
|
if (connection) {
|
||||||
|
if (profileReply->error() == QNetworkReply::NoError) {
|
||||||
// call helper method to get cookieHeaders
|
// call helper method to get cookieHeaders
|
||||||
Headers cookieHeaders = setupCookieHeadersFromProfileReply(profileReply);
|
Headers cookieHeaders = setupCookieHeadersFromProfileReply(profileReply);
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode302, QByteArray(),
|
connection->respond(HTTPConnection::StatusCode302, QByteArray(),
|
||||||
HTTPConnection::DefaultContentType, cookieHeaders);
|
HTTPConnection::DefaultContentType, cookieHeaders);
|
||||||
|
|
||||||
delete tokenReply;
|
|
||||||
delete profileReply;
|
|
||||||
|
|
||||||
// we've redirected the user back to our homepage
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// respond with a 200 code indicating that login is complete
|
|
||||||
connection->respond(HTTPConnection::StatusCode200);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
// the profile request failed, send back a 500 (assuming the connection is still around)
|
||||||
|
connection->respond(HTTPConnection::StatusCode500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profileReply->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2103,6 +2141,14 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
|
|
||||||
// the user does not have allowed username or role, return 401
|
// the user does not have allowed username or role, return 401
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
static const QByteArray REQUESTED_WITH_HEADER = "X-Requested-With";
|
||||||
|
static const QString XML_REQUESTED_WITH = "XMLHttpRequest";
|
||||||
|
|
||||||
|
if (connection->requestHeaders().value(REQUESTED_WITH_HEADER) == XML_REQUESTED_WITH) {
|
||||||
|
// unauthorized XHR requests get a 401 and not a 302, since there isn't an XHR
|
||||||
|
// path to OAuth authorize
|
||||||
|
connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY);
|
||||||
} else {
|
} else {
|
||||||
// re-direct this user to OAuth page
|
// re-direct this user to OAuth page
|
||||||
|
|
||||||
|
@ -2120,6 +2166,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode302,
|
connection->respond(HTTPConnection::StatusCode302,
|
||||||
QByteArray(), HTTPConnection::DefaultContentType, redirectHeaders);
|
QByteArray(), HTTPConnection::DefaultContentType, redirectHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
// we don't know about this user yet, so they are not yet authenticated
|
// we don't know about this user yet, so they are not yet authenticated
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -111,6 +111,9 @@ private slots:
|
||||||
void updateDownstreamNodes();
|
void updateDownstreamNodes();
|
||||||
void updateUpstreamNodes();
|
void updateUpstreamNodes();
|
||||||
|
|
||||||
|
void tokenGrantFinished();
|
||||||
|
void profileRequestFinished();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void iceServerChanged();
|
void iceServerChanged();
|
||||||
void userConnected();
|
void userConnected();
|
||||||
|
@ -178,6 +181,8 @@ private:
|
||||||
|
|
||||||
void updateReplicationNodes(ReplicationServerDirection direction);
|
void updateReplicationNodes(ReplicationServerDirection direction);
|
||||||
|
|
||||||
|
HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply);
|
||||||
|
|
||||||
SubnetList _acSubnetWhitelist;
|
SubnetList _acSubnetWhitelist;
|
||||||
|
|
||||||
std::vector<QString> _replicatedUsernames;
|
std::vector<QString> _replicatedUsernames;
|
||||||
|
@ -235,6 +240,8 @@ private:
|
||||||
|
|
||||||
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
|
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
|
||||||
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
||||||
|
|
||||||
|
QHash<QUuid, QPointer<HTTPSConnection>> _pendingOAuthConnections;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1198,6 +1198,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
||||||
static const QString SECURITY_ROOT_KEY = "security";
|
static const QString SECURITY_ROOT_KEY = "security";
|
||||||
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
|
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
|
||||||
static const QString BROADCASTING_KEY = "broadcasting";
|
static const QString BROADCASTING_KEY = "broadcasting";
|
||||||
|
static const QString DESCRIPTION_ROOT_KEY = "descriptors";
|
||||||
|
|
||||||
auto& settingsVariant = _configMap.getConfig();
|
auto& settingsVariant = _configMap.getConfig();
|
||||||
bool needRestart = false;
|
bool needRestart = false;
|
||||||
|
@ -1249,7 +1250,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
||||||
|
|
||||||
if (!matchingDescriptionObject.isEmpty()) {
|
if (!matchingDescriptionObject.isEmpty()) {
|
||||||
updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject);
|
updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject);
|
||||||
if (rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY) {
|
if (rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY && rootKey != SETTINGS_PATHS_KEY ) {
|
||||||
needRestart = true;
|
needRestart = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1265,7 +1266,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
||||||
if (!matchingDescriptionObject.isEmpty()) {
|
if (!matchingDescriptionObject.isEmpty()) {
|
||||||
const QJsonValue& settingValue = rootValue.toObject()[settingKey];
|
const QJsonValue& settingValue = rootValue.toObject()[settingKey];
|
||||||
updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject);
|
updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject);
|
||||||
if ((rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY)
|
if ((rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY && rootKey != DESCRIPTION_ROOT_KEY)
|
||||||
|| settingKey == AC_SUBNET_WHITELIST_KEY) {
|
|| settingKey == AC_SUBNET_WHITELIST_KEY) {
|
||||||
needRestart = true;
|
needRestart = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,36 +34,32 @@
|
||||||
{ "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" },
|
{ "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" },
|
||||||
{ "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" },
|
{ "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" },
|
||||||
|
|
||||||
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] },
|
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand"},
|
||||||
{ "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] },
|
{ "from": "Vive.RightHand", "to": "Standard.RightHand"},
|
||||||
|
|
||||||
{
|
{
|
||||||
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
|
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
|
||||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
|
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
|
||||||
"when": [ "Application.InHMD" ]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"from": "Vive.RightFoot", "to" : "Standard.RightFoot",
|
"from": "Vive.RightFoot", "to" : "Standard.RightFoot",
|
||||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
|
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
|
||||||
"when": [ "Application.InHMD" ]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"from": "Vive.Hips", "to" : "Standard.Hips",
|
"from": "Vive.Hips", "to" : "Standard.Hips",
|
||||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
|
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
|
||||||
"when": [ "Application.InHMD" ]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"from": "Vive.Spine2", "to" : "Standard.Spine2",
|
"from": "Vive.Spine2", "to" : "Standard.Spine2",
|
||||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
|
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
|
||||||
"when": [ "Application.InHMD" ]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{ "from": "Vive.Head", "to" : "Standard.Head", "when": [ "Application.InHMD" ] },
|
{ "from": "Vive.Head", "to" : "Standard.Head"},
|
||||||
|
|
||||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm", "when": [ "Application.InHMD" ] },
|
{ "from": "Vive.RightArm", "to" : "Standard.RightArm"},
|
||||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when": [ "Application.InHMD" ] }
|
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
BIN
interface/resources/images/calibration-help.png
Normal file
BIN
interface/resources/images/calibration-help.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
64
interface/resources/qml/controls-uit/ImageMessageBox.qml
Normal file
64
interface/resources/qml/controls-uit/ImageMessageBox.qml
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// ImageMessageBox.qml
|
||||||
|
//
|
||||||
|
// Created by Dante Ruiz on 7/5/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.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import "../styles-uit"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: imageBox
|
||||||
|
visible: false
|
||||||
|
anchors.fill: parent
|
||||||
|
property alias source: image.source
|
||||||
|
property alias imageWidth: image.width
|
||||||
|
property alias imageHeight: image.height
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: image
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: closeGlyphButton
|
||||||
|
text: hifi.glyphs.close
|
||||||
|
size: 25
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 15
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
parent.text = hifi.glyphs.closeInverted;
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: {
|
||||||
|
parent.text = hifi.glyphs.close;
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
imageBox.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1101,9 +1101,9 @@ Rectangle {
|
||||||
case 'nearbyUsers':
|
case 'nearbyUsers':
|
||||||
var data = message.params;
|
var data = message.params;
|
||||||
var index = -1;
|
var index = -1;
|
||||||
|
iAmAdmin = Users.canKick;
|
||||||
index = findNearbySessionIndex('', data);
|
index = findNearbySessionIndex('', data);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
iAmAdmin = Users.canKick;
|
|
||||||
myData = data[index];
|
myData = data[index];
|
||||||
data.splice(index, 1);
|
data.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -65,7 +65,7 @@ Rectangle {
|
||||||
|
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
id: image
|
id: image
|
||||||
text: hifi.glyphs.avatar1
|
text: hifi.glyphs.avatarTPose
|
||||||
size: 190
|
size: 190
|
||||||
color: hifi.colors.white
|
color: hifi.colors.white
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import "../../controls-uit" as HifiControls
|
||||||
StackView {
|
StackView {
|
||||||
id: stack
|
id: stack
|
||||||
initialItem: inputConfiguration
|
initialItem: inputConfiguration
|
||||||
|
property alias messageVisible: imageMessageBox.visible
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: inputConfiguration
|
id: inputConfiguration
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -26,6 +27,15 @@ StackView {
|
||||||
|
|
||||||
property var pluginSettings: null
|
property var pluginSettings: null
|
||||||
|
|
||||||
|
HifiControls.ImageMessageBox {
|
||||||
|
id: imageMessageBox
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 2000
|
||||||
|
imageWidth: 442
|
||||||
|
imageHeight: 670
|
||||||
|
source: "../../../images/calibration-help.png"
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: inputConfiguration.width
|
width: inputConfiguration.width
|
||||||
height: 1
|
height: 1
|
||||||
|
|
|
@ -54,6 +54,9 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
|
@ -64,6 +67,7 @@ Rectangle {
|
||||||
mouse.accepted = false;
|
mouse.accepted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
color: hifi.colors.baseGray
|
color: hifi.colors.baseGray
|
||||||
|
|
||||||
RalewayBold {
|
RalewayBold {
|
||||||
|
@ -146,6 +150,7 @@ Rectangle {
|
||||||
label: "Y: offset"
|
label: "Y: offset"
|
||||||
minimumValue: -10
|
minimumValue: -10
|
||||||
stepSize: 0.0254
|
stepSize: 0.0254
|
||||||
|
value: -0.05
|
||||||
colorScheme: hifi.colorSchemes.dark
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
|
@ -161,6 +166,7 @@ Rectangle {
|
||||||
minimumValue: -10
|
minimumValue: -10
|
||||||
stepSize: 0.0254
|
stepSize: 0.0254
|
||||||
decimals: 4
|
decimals: 4
|
||||||
|
value: -0.05
|
||||||
colorScheme: hifi.colorSchemes.dark
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
|
@ -290,6 +296,52 @@ Rectangle {
|
||||||
anchors.leftMargin: leftMargin
|
anchors.leftMargin: leftMargin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RalewayRegular {
|
||||||
|
id: info
|
||||||
|
|
||||||
|
text: "See Recommended Tracker Placement"
|
||||||
|
color: hifi.colors.blueHighlight
|
||||||
|
size: 10
|
||||||
|
anchors {
|
||||||
|
left: additional.right
|
||||||
|
leftMargin: 10
|
||||||
|
verticalCenter: additional.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: selected
|
||||||
|
color: hifi.colors.blueHighlight
|
||||||
|
|
||||||
|
width: info.width
|
||||||
|
height: 1
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: info.bottom
|
||||||
|
topMargin: 1
|
||||||
|
left: info.left
|
||||||
|
right: info.right
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent;
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
selected.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: {
|
||||||
|
selected.visible = false;
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
stack.messageVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: feetConfig
|
id: feetConfig
|
||||||
anchors.top: additional.bottom
|
anchors.top: additional.bottom
|
||||||
|
@ -379,6 +431,7 @@ Rectangle {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
hipBox.checked = true;
|
hipBox.checked = true;
|
||||||
feetBox.checked = true;
|
feetBox.checked = true;
|
||||||
|
shoulderBox.checked = false;
|
||||||
}
|
}
|
||||||
sendConfigurationSettings();
|
sendConfigurationSettings();
|
||||||
}
|
}
|
||||||
|
@ -416,6 +469,7 @@ Rectangle {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
hipBox.checked = true;
|
hipBox.checked = true;
|
||||||
feetBox.checked = true;
|
feetBox.checked = true;
|
||||||
|
chestBox.checked = false;
|
||||||
}
|
}
|
||||||
sendConfigurationSettings();
|
sendConfigurationSettings();
|
||||||
}
|
}
|
||||||
|
@ -642,6 +696,57 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
id: advanceSeperator
|
||||||
|
width: parent.width
|
||||||
|
anchors.top: timeToCalibrate.bottom
|
||||||
|
anchors.topMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewayBold {
|
||||||
|
id: advanceSettings
|
||||||
|
|
||||||
|
text: "Advanced Settings"
|
||||||
|
size: 12
|
||||||
|
|
||||||
|
color: hifi.colors.white
|
||||||
|
|
||||||
|
anchors.top: advanceSeperator.bottom
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: leftMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
id: viveInDesktop
|
||||||
|
width: 15
|
||||||
|
height: 15
|
||||||
|
boxRadius: 7
|
||||||
|
|
||||||
|
anchors.top: advanceSettings.bottom
|
||||||
|
anchors.topMargin: 5
|
||||||
|
anchors.left: openVrConfiguration.left
|
||||||
|
anchors.leftMargin: leftMargin + 10
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
sendConfigurationSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewayBold {
|
||||||
|
id: viveDesktopText
|
||||||
|
size: 10
|
||||||
|
text: "Use Vive devices in desktop mode"
|
||||||
|
color: hifi.colors.white
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: viveInDesktop.right
|
||||||
|
leftMargin: 5
|
||||||
|
verticalCenter: viveInDesktop.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: numberAnimation
|
id: numberAnimation
|
||||||
target: openVrConfiguration
|
target: openVrConfiguration
|
||||||
|
@ -728,6 +833,7 @@ Rectangle {
|
||||||
|
|
||||||
var HmdHead = settings["HMDHead"];
|
var HmdHead = settings["HMDHead"];
|
||||||
var viveController = settings["handController"];
|
var viveController = settings["handController"];
|
||||||
|
var desktopMode = settings["desktopMode"];
|
||||||
|
|
||||||
if (HmdHead) {
|
if (HmdHead) {
|
||||||
headBox.checked = true;
|
headBox.checked = true;
|
||||||
|
@ -745,6 +851,8 @@ Rectangle {
|
||||||
handBox.checked = false;
|
handBox.checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viveInDesktop.checked = desktopMode;
|
||||||
|
|
||||||
initializeButtonState();
|
initializeButtonState();
|
||||||
updateCalibrationText();
|
updateCalibrationText();
|
||||||
|
|
||||||
|
@ -901,7 +1009,8 @@ Rectangle {
|
||||||
var settingsObject = {
|
var settingsObject = {
|
||||||
"bodyConfiguration": trackerConfiguration,
|
"bodyConfiguration": trackerConfiguration,
|
||||||
"headConfiguration": headObject,
|
"headConfiguration": headObject,
|
||||||
"handConfiguration": handObject
|
"handConfiguration": handObject,
|
||||||
|
"desktopMode": viveInDesktop.checked
|
||||||
}
|
}
|
||||||
|
|
||||||
return settingsObject;
|
return settingsObject;
|
||||||
|
|
|
@ -98,6 +98,16 @@ StackView {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
propagateComposedEvents: true
|
||||||
|
onPressed: {
|
||||||
|
parent.forceActiveFocus();
|
||||||
|
addressBarDialog.keyboardEnabled = false;
|
||||||
|
mouse.accepted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
right: parent.right
|
right: parent.right
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
@ -227,9 +237,9 @@ StackView {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!addressLine.focus || !HMD.active) {
|
|
||||||
addressLine.focus = true;
|
addressLine.focus = true;
|
||||||
addressLine.forceActiveFocus();
|
addressLine.forceActiveFocus();
|
||||||
|
if (HMD.active) {
|
||||||
addressBarDialog.keyboardEnabled = HMD.active;
|
addressBarDialog.keyboardEnabled = HMD.active;
|
||||||
}
|
}
|
||||||
tabletRoot.playButtonClickSound();
|
tabletRoot.playButtonClickSound();
|
||||||
|
|
|
@ -52,10 +52,12 @@ Windows.ScrollingWindow {
|
||||||
|
|
||||||
// used to receive messages from interface script
|
// used to receive messages from interface script
|
||||||
function fromScript(message) {
|
function fromScript(message) {
|
||||||
|
if (loader.item !== null) {
|
||||||
if (loader.item.hasOwnProperty("fromScript")) {
|
if (loader.item.hasOwnProperty("fromScript")) {
|
||||||
loader.item.fromScript(message);
|
loader.item.fromScript(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SoundEffect {
|
SoundEffect {
|
||||||
id: buttonClickSound
|
id: buttonClickSound
|
||||||
|
|
|
@ -336,5 +336,6 @@ Item {
|
||||||
readonly property string source: "\ue01c"
|
readonly property string source: "\ue01c"
|
||||||
readonly property string playback_play: "\ue01d"
|
readonly property string playback_play: "\ue01d"
|
||||||
readonly property string stop_square: "\ue01e"
|
readonly property string stop_square: "\ue01e"
|
||||||
|
readonly property string avatarTPose: "\ue01f"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -953,7 +953,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
// Make sure we don't time out during slow operations at startup
|
// Make sure we don't time out during slow operations at startup
|
||||||
updateHeartbeat();
|
updateHeartbeat();
|
||||||
|
|
||||||
|
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||||
|
|
||||||
|
// once the settings have been loaded, check if we need to flip the default for UserActivityLogger
|
||||||
|
auto& userActivityLogger = UserActivityLogger::getInstance();
|
||||||
|
if (!userActivityLogger.isDisabledSettingSet()) {
|
||||||
|
// the user activity logger is opt-out for Interface
|
||||||
|
// but it's defaulted to disabled for other targets
|
||||||
|
// so we need to enable it here if it has never been disabled by the user
|
||||||
|
userActivityLogger.disable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userActivityLogger.isEnabled()) {
|
||||||
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||||
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||||
static const QString TESTER = "HIFI_TESTER";
|
static const QString TESTER = "HIFI_TESTER";
|
||||||
|
@ -997,14 +1008,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
|
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add firstRun flag from settings to launch event
|
|
||||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
|
||||||
properties["first_run"] = firstRun.get();
|
properties["first_run"] = firstRun.get();
|
||||||
|
|
||||||
// add the user's machine ID to the launch event
|
// add the user's machine ID to the launch event
|
||||||
properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||||
|
|
||||||
UserActivityLogger::getInstance().logAction("launch", properties);
|
userActivityLogger.logAction("launch", properties);
|
||||||
|
}
|
||||||
|
|
||||||
// Tell our entity edit sender about our known jurisdictions
|
// Tell our entity edit sender about our known jurisdictions
|
||||||
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
|
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
|
||||||
|
@ -3158,59 +3168,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
case Qt::Key_H: {
|
|
||||||
// whenever switching to/from full screen mirror from the keyboard, remember
|
|
||||||
// the state you were in before full screen mirror, and return to that.
|
|
||||||
auto previousMode = _myCamera.getMode();
|
|
||||||
if (previousMode != CAMERA_MODE_MIRROR) {
|
|
||||||
switch (previousMode) {
|
|
||||||
case CAMERA_MODE_FIRST_PERSON:
|
|
||||||
_returnFromFullScreenMirrorTo = MenuOption::FirstPerson;
|
|
||||||
break;
|
|
||||||
case CAMERA_MODE_THIRD_PERSON:
|
|
||||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// FIXME - it's not clear that these modes make sense to return to...
|
|
||||||
case CAMERA_MODE_INDEPENDENT:
|
|
||||||
_returnFromFullScreenMirrorTo = MenuOption::IndependentMode;
|
|
||||||
break;
|
|
||||||
case CAMERA_MODE_ENTITY:
|
|
||||||
_returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked);
|
|
||||||
if (isMirrorChecked) {
|
|
||||||
|
|
||||||
// if we got here without coming in from a non-Full Screen mirror case, then our
|
|
||||||
// _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old
|
|
||||||
// behavior of returning to ThirdPerson
|
|
||||||
if (_returnFromFullScreenMirrorTo.isEmpty()) {
|
|
||||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
|
||||||
}
|
|
||||||
Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true);
|
|
||||||
}
|
|
||||||
cameraMenuChanged();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_P: {
|
|
||||||
if (!(isShifted || isMeta || isOption)) {
|
|
||||||
bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson);
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !isFirstPersonChecked);
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, isFirstPersonChecked);
|
|
||||||
cameraMenuChanged();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_Slash:
|
case Qt::Key_Slash:
|
||||||
Menu::getInstance()->triggerOption(MenuOption::Stats);
|
Menu::getInstance()->triggerOption(MenuOption::Stats);
|
||||||
break;
|
break;
|
||||||
|
@ -7127,6 +7084,12 @@ void Application::updateDisplayMode() {
|
||||||
// reset the avatar, to set head and hand palms back to a reasonable default pose.
|
// reset the avatar, to set head and hand palms back to a reasonable default pose.
|
||||||
getMyAvatar()->reset(false);
|
getMyAvatar()->reset(false);
|
||||||
|
|
||||||
|
// switch to first person if entering hmd and setting is checked
|
||||||
|
if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) {
|
||||||
|
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||||
|
cameraMenuChanged();
|
||||||
|
}
|
||||||
|
|
||||||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@ Menu::Menu() {
|
||||||
|
|
||||||
// View > First Person
|
// View > First Person
|
||||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
MenuOption::FirstPerson, 0,
|
||||||
true, qApp, SLOT(cameraMenuChanged())));
|
true, qApp, SLOT(cameraMenuChanged())));
|
||||||
|
|
||||||
// View > Third Person
|
// View > Third Person
|
||||||
|
@ -233,7 +233,7 @@ Menu::Menu() {
|
||||||
|
|
||||||
// View > Mirror
|
// View > Mirror
|
||||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||||
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
MenuOption::FullscreenMirror, 0,
|
||||||
false, qApp, SLOT(cameraMenuChanged())));
|
false, qApp, SLOT(cameraMenuChanged())));
|
||||||
|
|
||||||
// View > Independent [advanced]
|
// View > Independent [advanced]
|
||||||
|
@ -258,6 +258,9 @@ Menu::Menu() {
|
||||||
// View > Overlays
|
// View > Overlays
|
||||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Overlays, 0, true);
|
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Overlays, 0, true);
|
||||||
|
|
||||||
|
// View > Enter First Person Mode in HMD
|
||||||
|
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPersonHMD, 0, true);
|
||||||
|
|
||||||
// Navigate menu ----------------------------------
|
// Navigate menu ----------------------------------
|
||||||
MenuWrapper* navigateMenu = addMenu("Navigate");
|
MenuWrapper* navigateMenu = addMenu("Navigate");
|
||||||
|
|
||||||
|
@ -319,7 +322,7 @@ Menu::Menu() {
|
||||||
QString("../../hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
|
QString("../../hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
|
||||||
});
|
});
|
||||||
|
|
||||||
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings");
|
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings...");
|
||||||
connect(action, &QAction::triggered, [] {
|
connect(action, &QAction::triggered, [] {
|
||||||
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
|
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
|
||||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||||
|
|
|
@ -105,6 +105,7 @@ namespace MenuOption {
|
||||||
const QString ExpandPhysicsSimulationTiming = "Expand /physics";
|
const QString ExpandPhysicsSimulationTiming = "Expand /physics";
|
||||||
const QString ExpandUpdateTiming = "Expand /update";
|
const QString ExpandUpdateTiming = "Expand /update";
|
||||||
const QString FirstPerson = "First Person";
|
const QString FirstPerson = "First Person";
|
||||||
|
const QString FirstPersonHMD = "Enter First Person Mode in HMD";
|
||||||
const QString FivePointCalibration = "5 Point Calibration";
|
const QString FivePointCalibration = "5 Point Calibration";
|
||||||
const QString FixGaze = "Fix Gaze (no saccade)";
|
const QString FixGaze = "Fix Gaze (no saccade)";
|
||||||
const QString Forward = "Forward";
|
const QString Forward = "Forward";
|
||||||
|
|
|
@ -1634,7 +1634,8 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
||||||
_characterController.setParentVelocity(parentVelocity);
|
_characterController.setParentVelocity(parentVelocity);
|
||||||
|
|
||||||
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
|
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
|
||||||
if (qApp->isHMDMode()) {
|
auto headPose = getHeadControllerPoseInAvatarFrame();
|
||||||
|
if (headPose.isValid()) {
|
||||||
_follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput());
|
_follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput());
|
||||||
} else {
|
} else {
|
||||||
_follow.deactivate();
|
_follow.deactivate();
|
||||||
|
|
|
@ -307,6 +307,7 @@ void Overlays::deleteOverlay(OverlayID id) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
_overlaysToDelete.push_back(overlayToDelete);
|
_overlaysToDelete.push_back(overlayToDelete);
|
||||||
emit overlayDeleted(id);
|
emit overlayDeleted(id);
|
||||||
}
|
}
|
||||||
|
@ -606,22 +607,16 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlay::Pointer thisOverlay;
|
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||||
{
|
|
||||||
QMutexLocker locker(&_mutex);
|
|
||||||
thisOverlay = _overlaysHUD[id];
|
|
||||||
}
|
|
||||||
if (thisOverlay) {
|
if (thisOverlay) {
|
||||||
|
if (thisOverlay->is3D()) {
|
||||||
|
if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) {
|
||||||
|
return text3dOverlay->textSize(text);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
|
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
|
||||||
return textOverlay->textSize(text);
|
return textOverlay->textSize(text);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&_mutex);
|
|
||||||
thisOverlay = _overlaysWorld[id];
|
|
||||||
}
|
|
||||||
if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) {
|
|
||||||
return text3dOverlay->textSize(text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QSizeF(0.0f, 0.0f);
|
return QSizeF(0.0f, 0.0f);
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool isEnabled() { return !_disabled.get(); }
|
bool isEnabled() { return !_disabled.get(); }
|
||||||
|
bool isDisabledSettingSet() const { return _disabled.isSet(); }
|
||||||
|
|
||||||
void disable(bool disable);
|
void disable(bool disable);
|
||||||
void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters());
|
void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters());
|
||||||
|
@ -53,7 +54,7 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UserActivityLogger();
|
UserActivityLogger();
|
||||||
Setting::Handle<bool> _disabled { "UserActivityLoggerDisabled", false };
|
Setting::Handle<bool> _disabled { "UserActivityLoggerDisabled", true };
|
||||||
|
|
||||||
QElapsedTimer _timer;
|
QElapsedTimer _timer;
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,13 +32,6 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
if (info.getType() == SHAPE_TYPE_NONE) {
|
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
|
||||||
if (4.0f * glm::length2(info.getHalfExtents()) < MIN_SHAPE_DIAGONAL_SQUARED) {
|
|
||||||
// tiny shapes are not supported
|
|
||||||
// qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoubleHashKey key = info.getHash();
|
DoubleHashKey key = info.getHash();
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
|
|
|
@ -107,6 +107,7 @@ namespace Setting {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSet() const {
|
bool isSet() const {
|
||||||
|
maybeInit();
|
||||||
return _isSet;
|
return _isSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
|
|
||||||
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER
|
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER
|
||||||
|
|
||||||
|
// Bullet doesn't support arbitrarily small shapes
|
||||||
|
const float MIN_HALF_EXTENT = 0.005f; // 0.5 cm
|
||||||
|
|
||||||
void ShapeInfo::clear() {
|
void ShapeInfo::clear() {
|
||||||
_url.clear();
|
_url.clear();
|
||||||
_pointCollection.clear();
|
_pointCollection.clear();
|
||||||
|
@ -26,14 +29,20 @@ void ShapeInfo::clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
|
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
|
||||||
|
_url = "";
|
||||||
_type = type;
|
_type = type;
|
||||||
_halfExtents = halfExtents;
|
setHalfExtents(halfExtents);
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case SHAPE_TYPE_NONE:
|
case SHAPE_TYPE_NONE:
|
||||||
_halfExtents = glm::vec3(0.0f);
|
_halfExtents = glm::vec3(0.0f);
|
||||||
break;
|
break;
|
||||||
case SHAPE_TYPE_BOX:
|
case SHAPE_TYPE_BOX:
|
||||||
case SHAPE_TYPE_SPHERE:
|
break;
|
||||||
|
case SHAPE_TYPE_SPHERE: {
|
||||||
|
float radius = glm::length(halfExtents) / SQUARE_ROOT_OF_3;
|
||||||
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||||
|
_halfExtents = glm::vec3(radius);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SHAPE_TYPE_COMPOUND:
|
case SHAPE_TYPE_COMPOUND:
|
||||||
case SHAPE_TYPE_STATIC_MESH:
|
case SHAPE_TYPE_STATIC_MESH:
|
||||||
|
@ -48,14 +57,15 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
||||||
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
||||||
_url = "";
|
_url = "";
|
||||||
_type = SHAPE_TYPE_BOX;
|
_type = SHAPE_TYPE_BOX;
|
||||||
_halfExtents = halfExtents;
|
setHalfExtents(halfExtents);
|
||||||
_doubleHashKey.clear();
|
_doubleHashKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setSphere(float radius) {
|
void ShapeInfo::setSphere(float radius) {
|
||||||
_url = "";
|
_url = "";
|
||||||
_type = SHAPE_TYPE_SPHERE;
|
_type = SHAPE_TYPE_SPHERE;
|
||||||
_halfExtents = glm::vec3(radius, radius, radius);
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||||
|
_halfExtents = glm::vec3(radius);
|
||||||
_doubleHashKey.clear();
|
_doubleHashKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +78,8 @@ void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollec
|
||||||
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
||||||
_url = "";
|
_url = "";
|
||||||
_type = SHAPE_TYPE_CAPSULE_Y;
|
_type = SHAPE_TYPE_CAPSULE_Y;
|
||||||
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||||
|
halfHeight = glm::max(halfHeight, 0.0f);
|
||||||
_halfExtents = glm::vec3(radius, halfHeight, radius);
|
_halfExtents = glm::vec3(radius, halfHeight, radius);
|
||||||
_doubleHashKey.clear();
|
_doubleHashKey.clear();
|
||||||
}
|
}
|
||||||
|
@ -239,3 +251,7 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
||||||
}
|
}
|
||||||
return _doubleHashKey;
|
return _doubleHashKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) {
|
||||||
|
_halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT));
|
||||||
|
}
|
||||||
|
|
|
@ -89,6 +89,8 @@ public:
|
||||||
const DoubleHashKey& getHash() const;
|
const DoubleHashKey& getHash() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void setHalfExtents(const glm::vec3& halfExtents);
|
||||||
|
|
||||||
QUrl _url; // url for model of convex collision hulls
|
QUrl _url; // url for model of convex collision hulls
|
||||||
PointCollection _pointCollection;
|
PointCollection _pointCollection;
|
||||||
TriangleIndices _triangleIndices;
|
TriangleIndices _triangleIndices;
|
||||||
|
|
|
@ -82,6 +82,12 @@ struct PoseData {
|
||||||
angularVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vAngularVelocity));
|
angularVelocities[i] = transformVectorFast(resetMat, toGlm(vrPoses[i].vAngularVelocity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resetToInvalid() {
|
||||||
|
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
|
||||||
|
vrPoses[i].bPoseIsValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME remove once OpenVR header is updated
|
// FIXME remove once OpenVR header is updated
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <glm/ext.hpp>
|
#include <glm/ext.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
#include <ui-plugins/PluginContainer.h>
|
||||||
|
#include <plugins/DisplayPlugin.h>
|
||||||
|
|
||||||
#include <controllers/UserInputMapper.h>
|
#include <controllers/UserInputMapper.h>
|
||||||
#include <Plugins/InputConfiguration.h>
|
#include <Plugins/InputConfiguration.h>
|
||||||
|
@ -60,11 +62,6 @@ static const int SECOND_FOOT = 1;
|
||||||
static const int HIP = 2;
|
static const int HIP = 2;
|
||||||
static const int CHEST = 3;
|
static const int CHEST = 3;
|
||||||
|
|
||||||
static float HEAD_PUCK_Y_OFFSET = -0.0254f;
|
|
||||||
static float HEAD_PUCK_Z_OFFSET = -0.152f;
|
|
||||||
static float HAND_PUCK_Y_OFFSET = -0.0508f;
|
|
||||||
static float HAND_PUCK_Z_OFFSET = 0.0254f;
|
|
||||||
|
|
||||||
const char* ViveControllerManager::NAME { "OpenVR" };
|
const char* ViveControllerManager::NAME { "OpenVR" };
|
||||||
|
|
||||||
const std::map<vr::ETrackingResult, QString> TRACKING_RESULT_TO_STRING = {
|
const std::map<vr::ETrackingResult, QString> TRACKING_RESULT_TO_STRING = {
|
||||||
|
@ -121,6 +118,29 @@ static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static glm::mat4 calculateResetMat() {
|
||||||
|
auto chaperone = vr::VRChaperone();
|
||||||
|
if (chaperone) {
|
||||||
|
float const UI_RADIUS = 1.0f;
|
||||||
|
float const UI_HEIGHT = 1.6f;
|
||||||
|
float const UI_Z_OFFSET = 0.5;
|
||||||
|
|
||||||
|
float xSize, zSize;
|
||||||
|
chaperone->GetPlayAreaSize(&xSize, &zSize);
|
||||||
|
glm::vec3 uiPos(0.0f, UI_HEIGHT, UI_RADIUS - (0.5f * zSize) - UI_Z_OFFSET);
|
||||||
|
|
||||||
|
return glm::inverse(createMatFromQuatAndPos(glm::quat(), uiPos));
|
||||||
|
}
|
||||||
|
return glm::mat4();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ViveControllerManager::isDesktopMode() {
|
||||||
|
if (_container) {
|
||||||
|
return !_container->getActiveDisplayPlugin()->isHmd();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void ViveControllerManager::calibrate() {
|
void ViveControllerManager::calibrate() {
|
||||||
if (isSupported()) {
|
if (isSupported()) {
|
||||||
_inputDevice->calibrateNextFrame();
|
_inputDevice->calibrateNextFrame();
|
||||||
|
@ -141,13 +161,21 @@ bool ViveControllerManager::isSupported() const {
|
||||||
|
|
||||||
void ViveControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) {
|
void ViveControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) {
|
||||||
if (isSupported()) {
|
if (isSupported()) {
|
||||||
|
if (configurationSettings.contains("desktopMode")) {
|
||||||
|
_desktopMode = configurationSettings["desktopMode"].toBool();
|
||||||
|
if (!_desktopMode) {
|
||||||
|
_resetMatCalculated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
_inputDevice->configureCalibrationSettings(configurationSettings);
|
_inputDevice->configureCalibrationSettings(configurationSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject ViveControllerManager::configurationSettings() {
|
QJsonObject ViveControllerManager::configurationSettings() {
|
||||||
if (isSupported()) {
|
if (isSupported()) {
|
||||||
return _inputDevice->configurationSettings();
|
QJsonObject configurationSettings = _inputDevice->configurationSettings();
|
||||||
|
configurationSettings["desktopMode"] = _desktopMode;
|
||||||
|
return configurationSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QJsonObject();
|
return QJsonObject();
|
||||||
|
@ -218,6 +246,18 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDesktopMode() && _desktopMode) {
|
||||||
|
if (!_resetMatCalculated) {
|
||||||
|
_resetMat = calculateResetMat();
|
||||||
|
_resetMatCalculated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0, _nextSimPoseData.vrPoses, vr::k_unMaxTrackedDeviceCount);
|
||||||
|
_nextSimPoseData.update(_resetMat);
|
||||||
|
} else if (isDesktopMode()) {
|
||||||
|
_nextSimPoseData.resetToInvalid();
|
||||||
|
}
|
||||||
|
|
||||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||||
handleOpenVrEvents();
|
handleOpenVrEvents();
|
||||||
if (openVrQuitRequested()) {
|
if (openVrQuitRequested()) {
|
||||||
|
@ -344,8 +384,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso
|
||||||
bool overrideHead = headObject["override"].toBool();
|
bool overrideHead = headObject["override"].toBool();
|
||||||
if (overrideHead) {
|
if (overrideHead) {
|
||||||
_headConfig = HeadConfig::Puck;
|
_headConfig = HeadConfig::Puck;
|
||||||
HEAD_PUCK_Y_OFFSET = headObject["Y"].toDouble();
|
_headPuckYOffset = headObject["Y"].toDouble();
|
||||||
HEAD_PUCK_Z_OFFSET = headObject["Z"].toDouble();
|
_headPuckZOffset = headObject["Z"].toDouble();
|
||||||
} else {
|
} else {
|
||||||
_headConfig = HeadConfig::HMD;
|
_headConfig = HeadConfig::HMD;
|
||||||
}
|
}
|
||||||
|
@ -354,8 +394,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso
|
||||||
bool overrideHands = handsObject["override"].toBool();
|
bool overrideHands = handsObject["override"].toBool();
|
||||||
if (overrideHands) {
|
if (overrideHands) {
|
||||||
_handConfig = HandConfig::Pucks;
|
_handConfig = HandConfig::Pucks;
|
||||||
HAND_PUCK_Y_OFFSET = handsObject["Y"].toDouble();
|
_handPuckYOffset = handsObject["Y"].toDouble();
|
||||||
HAND_PUCK_Z_OFFSET = handsObject["Z"].toDouble();
|
_handPuckZOffset = handsObject["Z"].toDouble();
|
||||||
} else {
|
} else {
|
||||||
_handConfig = HandConfig::HandController;
|
_handConfig = HandConfig::HandController;
|
||||||
}
|
}
|
||||||
|
@ -426,7 +466,9 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde
|
||||||
// transform into avatar frame
|
// transform into avatar frame
|
||||||
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
|
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
|
||||||
_poseStateMap[poseIndex] = pose.transform(controllerToAvatar);
|
_poseStateMap[poseIndex] = pose.transform(controllerToAvatar);
|
||||||
_validTrackedObjects.push_back(std::make_pair(poseIndex, _poseStateMap[poseIndex]));
|
|
||||||
|
// but _validTrackedObjects remain in sensor frame
|
||||||
|
_validTrackedObjects.push_back(std::make_pair(poseIndex, pose));
|
||||||
} else {
|
} else {
|
||||||
controller::Pose invalidPose;
|
controller::Pose invalidPose;
|
||||||
_poseStateMap[poseIndex] = invalidPose;
|
_poseStateMap[poseIndex] = invalidPose;
|
||||||
|
@ -474,6 +516,7 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
|
||||||
if (_headConfig == HeadConfig::HMD) {
|
if (_headConfig == HeadConfig::HMD) {
|
||||||
defaultToReferenceMat = calculateDefaultToReferenceForHmd(inputCalibration);
|
defaultToReferenceMat = calculateDefaultToReferenceForHmd(inputCalibration);
|
||||||
} else if (_headConfig == HeadConfig::Puck) {
|
} else if (_headConfig == HeadConfig::Puck) {
|
||||||
|
std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition);
|
||||||
defaultToReferenceMat = calculateDefaultToReferenceForHeadPuck(inputCalibration);
|
defaultToReferenceMat = calculateDefaultToReferenceForHeadPuck(inputCalibration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,63 +711,67 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defaultToReferenceMat is an offset from avatar space to sensor space.
|
||||||
|
// it aligns the default center-eye in avatar space with the hmd in sensor space.
|
||||||
|
//
|
||||||
|
// * E_a is the the default center-of-the-eyes transform in avatar space.
|
||||||
|
// * E_s is the the hmd eye-center transform in sensor space, with roll and pitch removed.
|
||||||
|
// * D is the defaultReferenceMat.
|
||||||
|
//
|
||||||
|
// E_s = D * E_a =>
|
||||||
|
// D = E_s * inverse(E_a)
|
||||||
|
//
|
||||||
glm::mat4 ViveControllerManager::InputDevice::calculateDefaultToReferenceForHmd(const controller::InputCalibrationData& inputCalibration) {
|
glm::mat4 ViveControllerManager::InputDevice::calculateDefaultToReferenceForHmd(const controller::InputCalibrationData& inputCalibration) {
|
||||||
// convert the hmd head from sensor space to avatar space
|
|
||||||
glm::mat4 hmdSensorFlippedMat = inputCalibration.hmdSensorMat * Matrices::Y_180;
|
|
||||||
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
|
|
||||||
glm::mat4 hmdAvatarMat = sensorToAvatarMat * hmdSensorFlippedMat;
|
|
||||||
|
|
||||||
// cancel the roll and pitch for the hmd head
|
// the center-eye transform in avatar space.
|
||||||
glm::quat hmdRotation = cancelOutRollAndPitch(glmExtractRotation(hmdAvatarMat));
|
glm::mat4 E_a = inputCalibration.defaultCenterEyeMat;
|
||||||
glm::vec3 hmdTranslation = extractTranslation(hmdAvatarMat);
|
|
||||||
glm::mat4 currentHmd = createMatFromQuatAndPos(hmdRotation, hmdTranslation);
|
|
||||||
|
|
||||||
// calculate the offset from the centerOfEye to defaultHeadMat
|
// the center-eye transform in sensor space.
|
||||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
|
glm::mat4 E_s = inputCalibration.hmdSensorMat * Matrices::Y_180; // the Y_180 is to convert hmd from -z forward to z forward.
|
||||||
|
|
||||||
glm::mat4 currentHead = currentHmd * defaultHeadOffset;
|
// cancel out roll and pitch on E_s
|
||||||
|
glm::quat rot = cancelOutRollAndPitch(glmExtractRotation(E_s));
|
||||||
|
glm::vec3 trans = extractTranslation(E_s);
|
||||||
|
E_s = createMatFromQuatAndPos(rot, trans);
|
||||||
|
|
||||||
// calculate the defaultToRefrenceXform
|
return E_s * glm::inverse(E_a);
|
||||||
glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
|
|
||||||
|
|
||||||
return defaultToReferenceMat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defaultToReferenceMat is an offset from avatar space to sensor space.
|
||||||
|
// It aligns the default center-of-the-eyes transform in avatar space with the head-puck in sensor space.
|
||||||
|
// The offset from the center-of-the-eyes to the head-puck can be configured via _headPuckYOffset and _headPuckZOffset,
|
||||||
|
// These values are exposed in the configuration UI.
|
||||||
|
//
|
||||||
|
// * E_a is the the default center-eye transform in avatar space.
|
||||||
|
// * E_s is the the head-puck center-eye transform in sensor space, with roll and pitch removed.
|
||||||
|
// * D is the defaultReferenceMat.
|
||||||
|
//
|
||||||
|
// E_s = D * E_a =>
|
||||||
|
// D = E_s * inverse(E_a)
|
||||||
|
//
|
||||||
glm::mat4 ViveControllerManager::InputDevice::calculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) {
|
glm::mat4 ViveControllerManager::InputDevice::calculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) {
|
||||||
glm::mat4 avatarToSensorMat = glm::inverse(inputCalibration.sensorToWorldMat) * inputCalibration.avatarMat;
|
|
||||||
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
|
// the center-eye transform in avatar space.
|
||||||
|
glm::mat4 E_a = inputCalibration.defaultCenterEyeMat;
|
||||||
|
|
||||||
|
// calculate the center-eye transform in sensor space, via the head-puck
|
||||||
size_t headPuckIndex = _validTrackedObjects.size() - 1;
|
size_t headPuckIndex = _validTrackedObjects.size() - 1;
|
||||||
controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second;
|
controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second;
|
||||||
glm::mat4 headPuckAvatarMat = createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()) * Matrices::Y_180;
|
|
||||||
glm::vec3 headPuckTranslation = extractTranslation(headPuckAvatarMat);
|
|
||||||
glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f);
|
|
||||||
glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
|
|
||||||
|
|
||||||
// check that the head puck z axis is not parrallel to the world up
|
// AJT: TODO: handle case were forward is parallel with UNIT_Y.
|
||||||
const float EPSILON = 1.0e-4f;
|
glm::vec3 forward = headPuckPose.rotation * -Vectors::UNIT_Z;
|
||||||
glm::vec3 zAxis = glmExtractRotation(headPuckAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f);
|
glm::vec3 x = glm::normalize(glm::cross(Vectors::UNIT_Y, forward));
|
||||||
if (fabsf(fabsf(glm::dot(glm::normalize(worldUp), glm::normalize(zAxis))) - 1.0f) < EPSILON) {
|
glm::vec3 z = glm::normalize(glm::cross(x, Vectors::UNIT_Y));
|
||||||
headPuckZAxis = glm::vec3(1.0f, 0.0f, 0.0f);
|
glm::mat3 centerEyeRotMat(x, Vectors::UNIT_Y, z);
|
||||||
}
|
glm::vec3 centerEyeTrans = headPuckPose.translation + centerEyeRotMat * glm::vec3(0.0f, _headPuckYOffset, _headPuckZOffset);
|
||||||
|
|
||||||
glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f);
|
glm::mat4 E_s(glm::vec4(centerEyeRotMat[0], 0.0f),
|
||||||
glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis));
|
glm::vec4(centerEyeRotMat[1], 0.0f),
|
||||||
glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime));
|
glm::vec4(centerEyeRotMat[2], 0.0f),
|
||||||
glm::mat4 newHeadPuck = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f),
|
glm::vec4(centerEyeTrans, 1.0f));
|
||||||
glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f));
|
|
||||||
|
|
||||||
glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f),
|
return E_s * glm::inverse(E_a);
|
||||||
glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, HEAD_PUCK_Y_OFFSET, HEAD_PUCK_Z_OFFSET, 1.0f));
|
|
||||||
|
|
||||||
glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset;
|
|
||||||
|
|
||||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
|
|
||||||
|
|
||||||
glm::mat4 currentHead = finalHeadPuck * defaultHeadOffset;
|
|
||||||
|
|
||||||
// calculate the defaultToRefrenceXform
|
|
||||||
glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
|
|
||||||
return defaultToReferenceMat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) {
|
void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) {
|
||||||
|
@ -914,7 +961,7 @@ void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToR
|
||||||
glm::vec4(zPrime, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
glm::vec4(zPrime, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
|
||||||
glm::vec3 translationOffset = glm::vec3(0.0f, HAND_PUCK_Y_OFFSET, HAND_PUCK_Z_OFFSET);
|
glm::vec3 translationOffset = glm::vec3(0.0f, _handPuckYOffset, _handPuckZOffset);
|
||||||
glm::quat initialRotation = glmExtractRotation(handPoseAvatarMat);
|
glm::quat initialRotation = glmExtractRotation(handPoseAvatarMat);
|
||||||
glm::quat finalRotation = glmExtractRotation(newHandMat);
|
glm::quat finalRotation = glmExtractRotation(newHandMat);
|
||||||
|
|
||||||
|
@ -945,7 +992,7 @@ void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultTo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
glm::vec3 translationOffset = glm::vec3(0.0f, HAND_PUCK_Y_OFFSET, HAND_PUCK_Z_OFFSET);
|
glm::vec3 translationOffset = glm::vec3(0.0f, _handPuckYOffset, _handPuckZOffset);
|
||||||
glm::quat initialRotation = glmExtractRotation(handPoseAvatarMat);
|
glm::quat initialRotation = glmExtractRotation(handPoseAvatarMat);
|
||||||
glm::quat finalRotation = glmExtractRotation(newHandMat);
|
glm::quat finalRotation = glmExtractRotation(newHandMat);
|
||||||
|
|
||||||
|
@ -1030,13 +1077,8 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo
|
||||||
void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
|
void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
|
||||||
size_t headIndex = _validTrackedObjects.size() - 1;
|
size_t headIndex = _validTrackedObjects.size() - 1;
|
||||||
const PuckPosePair& head = _validTrackedObjects[headIndex];
|
const PuckPosePair& head = _validTrackedObjects[headIndex];
|
||||||
|
|
||||||
// assume the person is wearing the head puck on his/her forehead
|
|
||||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
|
|
||||||
controller::Pose newHead = head.second.postTransform(defaultHeadOffset);
|
|
||||||
|
|
||||||
_jointToPuckMap[controller::HEAD] = head.first;
|
_jointToPuckMap[controller::HEAD] = head.first;
|
||||||
_pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead);
|
_pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, head.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ViveControllerManager::InputDevice::configToString(Config config) {
|
QString ViveControllerManager::InputDevice::configToString(Config config) {
|
||||||
|
|
|
@ -177,6 +177,10 @@ private:
|
||||||
float _leftHapticDuration { 0.0f };
|
float _leftHapticDuration { 0.0f };
|
||||||
float _rightHapticStrength { 0.0f };
|
float _rightHapticStrength { 0.0f };
|
||||||
float _rightHapticDuration { 0.0f };
|
float _rightHapticDuration { 0.0f };
|
||||||
|
float _headPuckYOffset { -0.05f };
|
||||||
|
float _headPuckZOffset { -0.05f };
|
||||||
|
float _handPuckYOffset { 0.0f };
|
||||||
|
float _handPuckZOffset { 0.0f };
|
||||||
bool _triggersPressedHandled { false };
|
bool _triggersPressedHandled { false };
|
||||||
bool _calibrated { false };
|
bool _calibrated { false };
|
||||||
bool _timeTilCalibrationSet { false };
|
bool _timeTilCalibrationSet { false };
|
||||||
|
@ -190,9 +194,12 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign);
|
void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign);
|
||||||
|
bool isDesktopMode();
|
||||||
bool _registeredWithInputMapper { false };
|
bool _registeredWithInputMapper { false };
|
||||||
bool _modelLoaded { false };
|
bool _modelLoaded { false };
|
||||||
|
bool _resetMatCalculated { false };
|
||||||
|
bool _desktopMode { false };
|
||||||
|
glm::mat4 _resetMat { glm::mat4() };
|
||||||
model::Geometry _modelGeometry;
|
model::Geometry _modelGeometry;
|
||||||
gpu::TexturePointer _texture;
|
gpu::TexturePointer _texture;
|
||||||
|
|
||||||
|
|
116
scripts/system/controllers/godView.js
Normal file
116
scripts/system/controllers/godView.js
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
"use strict";
|
||||||
|
//
|
||||||
|
// godView.js
|
||||||
|
// scripts/system/
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 1 Jun 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
|
||||||
|
//
|
||||||
|
/* globals HMD, Script, Menu, Tablet, Camera */
|
||||||
|
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||||
|
|
||||||
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
var godView = false;
|
||||||
|
|
||||||
|
var GOD_CAMERA_OFFSET = -1; // 1 meter below the avatar
|
||||||
|
var GOD_VIEW_HEIGHT = 300; // 300 meter above the ground
|
||||||
|
var ABOVE_GROUND_DROP = 2;
|
||||||
|
var MOVE_BY = 1;
|
||||||
|
|
||||||
|
function moveTo(position) {
|
||||||
|
if (godView) {
|
||||||
|
MyAvatar.position = position;
|
||||||
|
Camera.position = Vec3.sum(MyAvatar.position, {x:0, y: GOD_CAMERA_OFFSET, z: 0});
|
||||||
|
} else {
|
||||||
|
MyAvatar.position = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyPressEvent(event) {
|
||||||
|
if (godView) {
|
||||||
|
switch(event.text) {
|
||||||
|
case "UP":
|
||||||
|
moveTo(Vec3.sum(MyAvatar.position, {x:0.0, y: 0, z: -1 * MOVE_BY}));
|
||||||
|
break;
|
||||||
|
case "DOWN":
|
||||||
|
moveTo(Vec3.sum(MyAvatar.position, {x:0, y: 0, z: MOVE_BY}));
|
||||||
|
break;
|
||||||
|
case "LEFT":
|
||||||
|
moveTo(Vec3.sum(MyAvatar.position, {x:-1 * MOVE_BY, y: 0, z: 0}));
|
||||||
|
break;
|
||||||
|
case "RIGHT":
|
||||||
|
moveTo(Vec3.sum(MyAvatar.position, {x:MOVE_BY, y: 0, z: 0}));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mousePress(event) {
|
||||||
|
if (godView) {
|
||||||
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
|
var pointingAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,300));
|
||||||
|
var moveToPosition = { x: pointingAt.x, y: MyAvatar.position.y, z: pointingAt.z };
|
||||||
|
moveTo(moveToPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var oldCameraMode = Camera.mode;
|
||||||
|
|
||||||
|
function startGodView() {
|
||||||
|
if (!godView) {
|
||||||
|
oldCameraMode = Camera.mode;
|
||||||
|
MyAvatar.position = Vec3.sum(MyAvatar.position, {x:0, y: GOD_VIEW_HEIGHT, z: 0});
|
||||||
|
Camera.mode = "independent";
|
||||||
|
Camera.position = Vec3.sum(MyAvatar.position, {x:0, y: GOD_CAMERA_OFFSET, z: 0});
|
||||||
|
Camera.orientation = Quat.fromPitchYawRollDegrees(-90,0,0);
|
||||||
|
godView = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function endGodView() {
|
||||||
|
if (godView) {
|
||||||
|
Camera.mode = oldCameraMode;
|
||||||
|
MyAvatar.position = Vec3.sum(MyAvatar.position, {x:0, y: (-1 * GOD_VIEW_HEIGHT) + ABOVE_GROUND_DROP, z: 0});
|
||||||
|
godView = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var button;
|
||||||
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
|
||||||
|
function onClicked() {
|
||||||
|
if (godView) {
|
||||||
|
endGodView();
|
||||||
|
} else {
|
||||||
|
startGodView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button = tablet.addButton({
|
||||||
|
icon: "icons/tablet-icons/switch-desk-i.svg", // FIXME - consider a better icon from Alan
|
||||||
|
text: "God View"
|
||||||
|
});
|
||||||
|
|
||||||
|
button.clicked.connect(onClicked);
|
||||||
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
|
Controller.mousePressEvent.connect(mousePress);
|
||||||
|
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function () {
|
||||||
|
if (godView) {
|
||||||
|
endGodView();
|
||||||
|
}
|
||||||
|
button.clicked.disconnect(onClicked);
|
||||||
|
if (tablet) {
|
||||||
|
tablet.removeButton(button);
|
||||||
|
}
|
||||||
|
Controller.keyPressEvent.disconnect(keyPressEvent);
|
||||||
|
Controller.mousePressEvent.disconnect(mousePress);
|
||||||
|
});
|
||||||
|
|
||||||
|
}()); // END LOCAL_SCOPE
|
|
@ -1010,7 +1010,6 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Entity List...",
|
menuItemName: "Entity List...",
|
||||||
shortcutKey: "CTRL+META+L",
|
|
||||||
afterItem: "Entities",
|
afterItem: "Entities",
|
||||||
grouping: "Advanced"
|
grouping: "Advanced"
|
||||||
});
|
});
|
||||||
|
@ -1041,7 +1040,6 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Allow Selecting of Large Models",
|
menuItemName: "Allow Selecting of Large Models",
|
||||||
shortcutKey: "CTRL+META+L",
|
|
||||||
afterItem: GRABBABLE_ENTITIES_MENU_ITEM,
|
afterItem: GRABBABLE_ENTITIES_MENU_ITEM,
|
||||||
isCheckable: true,
|
isCheckable: true,
|
||||||
isChecked: true,
|
isChecked: true,
|
||||||
|
@ -1050,7 +1048,6 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Allow Selecting of Small Models",
|
menuItemName: "Allow Selecting of Small Models",
|
||||||
shortcutKey: "CTRL+META+S",
|
|
||||||
afterItem: "Allow Selecting of Large Models",
|
afterItem: "Allow Selecting of Large Models",
|
||||||
isCheckable: true,
|
isCheckable: true,
|
||||||
isChecked: true,
|
isChecked: true,
|
||||||
|
@ -1059,7 +1056,6 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Allow Selecting of Lights",
|
menuItemName: "Allow Selecting of Lights",
|
||||||
shortcutKey: "CTRL+SHIFT+META+L",
|
|
||||||
afterItem: "Allow Selecting of Small Models",
|
afterItem: "Allow Selecting of Small Models",
|
||||||
isCheckable: true,
|
isCheckable: true,
|
||||||
grouping: "Advanced"
|
grouping: "Advanced"
|
||||||
|
@ -1067,14 +1063,12 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Select All Entities In Box",
|
menuItemName: "Select All Entities In Box",
|
||||||
shortcutKey: "CTRL+SHIFT+META+A",
|
|
||||||
afterItem: "Allow Selecting of Lights",
|
afterItem: "Allow Selecting of Lights",
|
||||||
grouping: "Advanced"
|
grouping: "Advanced"
|
||||||
});
|
});
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Select All Entities Touching Box",
|
menuItemName: "Select All Entities Touching Box",
|
||||||
shortcutKey: "CTRL+SHIFT+META+T",
|
|
||||||
afterItem: "Select All Entities In Box",
|
afterItem: "Select All Entities In Box",
|
||||||
grouping: "Advanced"
|
grouping: "Advanced"
|
||||||
});
|
});
|
||||||
|
@ -1082,21 +1076,18 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Export Entities",
|
menuItemName: "Export Entities",
|
||||||
shortcutKey: "CTRL+META+E",
|
|
||||||
afterItem: "Entities",
|
afterItem: "Entities",
|
||||||
grouping: "Advanced"
|
grouping: "Advanced"
|
||||||
});
|
});
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Import Entities",
|
menuItemName: "Import Entities",
|
||||||
shortcutKey: "CTRL+META+I",
|
|
||||||
afterItem: "Export Entities",
|
afterItem: "Export Entities",
|
||||||
grouping: "Advanced"
|
grouping: "Advanced"
|
||||||
});
|
});
|
||||||
Menu.addMenuItem({
|
Menu.addMenuItem({
|
||||||
menuName: "Edit",
|
menuName: "Edit",
|
||||||
menuItemName: "Import Entities from URL",
|
menuItemName: "Import Entities from URL",
|
||||||
shortcutKey: "CTRL+META+U",
|
|
||||||
afterItem: "Import Entities",
|
afterItem: "Import Entities",
|
||||||
grouping: "Advanced"
|
grouping: "Advanced"
|
||||||
});
|
});
|
||||||
|
|
|
@ -482,10 +482,10 @@ function populateNearbyUserList(selectData, oldAudioData) {
|
||||||
isPresent: true,
|
isPresent: true,
|
||||||
isReplicated: avatar.isReplicated
|
isReplicated: avatar.isReplicated
|
||||||
};
|
};
|
||||||
if (id) {
|
|
||||||
addAvatarNode(id); // No overlay for ourselves
|
|
||||||
// Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin.
|
// Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin.
|
||||||
Users.requestUsernameFromID(id);
|
Users.requestUsernameFromID(id);
|
||||||
|
if (id) {
|
||||||
|
addAvatarNode(id); // No overlay for ourselves
|
||||||
avatarsOfInterest[id] = true;
|
avatarsOfInterest[id] = true;
|
||||||
} else {
|
} else {
|
||||||
// Return our username from the Account API
|
// Return our username from the Account API
|
||||||
|
@ -862,6 +862,9 @@ function avatarDisconnected(nodeID) {
|
||||||
|
|
||||||
function clearLocalQMLDataAndClosePAL() {
|
function clearLocalQMLDataAndClosePAL() {
|
||||||
sendToQml({ method: 'clearLocalQMLData' });
|
sendToQml({ method: 'clearLocalQMLData' });
|
||||||
|
if (onPalScreen) {
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function avatarAdded(avatarID) {
|
function avatarAdded(avatarID) {
|
||||||
|
|
|
@ -17,8 +17,8 @@ set_target_properties(ac-client PROPERTIES FOLDER "Tools")
|
||||||
add_subdirectory(skeleton-dump)
|
add_subdirectory(skeleton-dump)
|
||||||
set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools")
|
set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools")
|
||||||
|
|
||||||
add_subdirectory(atp-get)
|
add_subdirectory(atp-client)
|
||||||
set_target_properties(atp-get PROPERTIES FOLDER "Tools")
|
set_target_properties(atp-client PROPERTIES FOLDER "Tools")
|
||||||
|
|
||||||
add_subdirectory(oven)
|
add_subdirectory(oven)
|
||||||
set_target_properties(oven PROPERTIES FOLDER "Tools")
|
set_target_properties(oven PROPERTIES FOLDER "Tools")
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
#include <NetworkLogging.h>
|
#include <NetworkLogging.h>
|
||||||
|
#include <NetworkingConstants.h>
|
||||||
#include <SharedLogging.h>
|
#include <SharedLogging.h>
|
||||||
#include <AddressManager.h>
|
#include <AddressManager.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
@ -33,6 +34,9 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
||||||
const QCommandLineOption verboseOutput("v", "verbose output");
|
const QCommandLineOption verboseOutput("v", "verbose output");
|
||||||
parser.addOption(verboseOutput);
|
parser.addOption(verboseOutput);
|
||||||
|
|
||||||
|
const QCommandLineOption authOption("u", "set usename and pass", "username:password");
|
||||||
|
parser.addOption(authOption);
|
||||||
|
|
||||||
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
|
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
|
||||||
parser.addOption(domainAddressOption);
|
parser.addOption(domainAddressOption);
|
||||||
|
|
||||||
|
@ -42,7 +46,6 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
||||||
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
|
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
|
||||||
parser.addOption(listenPortOption);
|
parser.addOption(listenPortOption);
|
||||||
|
|
||||||
|
|
||||||
if (!parser.parse(QCoreApplication::arguments())) {
|
if (!parser.parse(QCoreApplication::arguments())) {
|
||||||
qCritical() << parser.errorText() << endl;
|
qCritical() << parser.errorText() << endl;
|
||||||
parser.showHelp();
|
parser.showHelp();
|
||||||
|
@ -67,6 +70,7 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
||||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
|
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString domainServerAddress = "127.0.0.1:40103";
|
QString domainServerAddress = "127.0.0.1:40103";
|
||||||
if (parser.isSet(domainAddressOption)) {
|
if (parser.isSet(domainAddressOption)) {
|
||||||
domainServerAddress = parser.value(domainAddressOption);
|
domainServerAddress = parser.value(domainAddressOption);
|
||||||
|
@ -81,6 +85,18 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
||||||
listenPort = parser.value(listenPortOption).toInt();
|
listenPort = parser.value(listenPortOption).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(authOption)) {
|
||||||
|
QStringList pieces = parser.value(authOption).split(":");
|
||||||
|
if (pieces.size() != 2) {
|
||||||
|
qDebug() << "-u should be followed by username:password";
|
||||||
|
parser.showHelp();
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
_username = pieces[0];
|
||||||
|
_password = pieces[1];
|
||||||
|
}
|
||||||
|
|
||||||
Setting::init();
|
Setting::init();
|
||||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||||
|
|
||||||
|
@ -88,6 +104,9 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
||||||
DependencyManager::set<AddressManager>();
|
DependencyManager::set<AddressManager>();
|
||||||
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
||||||
|
|
||||||
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
accountManager->setIsAgent(true);
|
||||||
|
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
@ -101,22 +120,40 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
||||||
nodeList->startThread();
|
nodeList->startThread();
|
||||||
|
|
||||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||||
|
|
||||||
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||||
// connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
|
|
||||||
// connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
|
||||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused);
|
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused);
|
||||||
|
|
||||||
connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded);
|
connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded);
|
||||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &ACClientApp::nodeKilled);
|
connect(nodeList.data(), &NodeList::nodeKilled, this, &ACClientApp::nodeKilled);
|
||||||
connect(nodeList.data(), &NodeList::nodeActivated, this, &ACClientApp::nodeActivated);
|
connect(nodeList.data(), &NodeList::nodeActivated, this, &ACClientApp::nodeActivated);
|
||||||
// connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
|
|
||||||
// connect(nodeList.data(), &NodeList::uuidChanged, this, &ACClientApp::setSessionUUID);
|
|
||||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch);
|
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch);
|
||||||
|
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||||
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
QString username = accountManager->getAccountInfo().getUsername();
|
||||||
|
qDebug() << "cached username is" << username << ", isLoggedIn =" << accountManager->isLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_username.isEmpty()) {
|
||||||
|
|
||||||
|
connect(accountManager.data(), &AccountManager::newKeypair, this, [&](){
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "new keypair has been created.";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(accountManager.data(), &AccountManager::loginComplete, this, [&](){
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "login successful";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(accountManager.data(), &AccountManager::loginFailed, this, [&](){
|
||||||
|
qDebug() << "login failed.";
|
||||||
|
});
|
||||||
|
accountManager->requestAccessToken(_username, _password);
|
||||||
|
}
|
||||||
|
|
||||||
DependencyManager::get<AddressManager>()->handleLookupString(domainServerAddress, false);
|
DependencyManager::get<AddressManager>()->handleLookupString(domainServerAddress, false);
|
||||||
|
|
||||||
QTimer* doTimer = new QTimer(this);
|
QTimer* doTimer = new QTimer(this);
|
||||||
|
|
|
@ -47,6 +47,9 @@ private:
|
||||||
bool _sawAvatarMixer { false };
|
bool _sawAvatarMixer { false };
|
||||||
bool _sawAssetServer { false };
|
bool _sawAssetServer { false };
|
||||||
bool _sawMessagesMixer { false };
|
bool _sawMessagesMixer { false };
|
||||||
|
|
||||||
|
QString _username;
|
||||||
|
QString _password;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //hifi_ACClientApp_h
|
#endif //hifi_ACClientApp_h
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
set(TARGET_NAME atp-get)
|
set(TARGET_NAME atp-client)
|
||||||
setup_hifi_project(Core Widgets)
|
setup_hifi_project(Core Widgets)
|
||||||
setup_memory_debugger()
|
setup_memory_debugger()
|
||||||
link_hifi_libraries(shared networking)
|
link_hifi_libraries(shared networking)
|
406
tools/atp-client/src/ATPClientApp.cpp
Normal file
406
tools/atp-client/src/ATPClientApp.cpp
Normal file
|
@ -0,0 +1,406 @@
|
||||||
|
//
|
||||||
|
// ATPClientApp.cpp
|
||||||
|
// tools/atp-client/src
|
||||||
|
//
|
||||||
|
// Created by Seth Alves on 2017-3-15
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
|
||||||
|
#include <NetworkLogging.h>
|
||||||
|
#include <NetworkingConstants.h>
|
||||||
|
#include <SharedLogging.h>
|
||||||
|
#include <AddressManager.h>
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include <SettingHandle.h>
|
||||||
|
#include <AssetUpload.h>
|
||||||
|
|
||||||
|
#include "ATPClientApp.h"
|
||||||
|
|
||||||
|
#define HIGH_FIDELITY_ATP_CLIENT_USER_AGENT "Mozilla/5.0 (HighFidelityATPClient)"
|
||||||
|
#define TIMEOUT_MILLISECONDS 8000
|
||||||
|
|
||||||
|
ATPClientApp::ATPClientApp(int argc, char* argv[]) :
|
||||||
|
QCoreApplication(argc, argv)
|
||||||
|
{
|
||||||
|
// parse command-line
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription("High Fidelity ATP-Client");
|
||||||
|
|
||||||
|
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||||
|
|
||||||
|
const QCommandLineOption verboseOutput("v", "verbose output");
|
||||||
|
parser.addOption(verboseOutput);
|
||||||
|
|
||||||
|
const QCommandLineOption uploadOption("T", "upload local file", "local-file-to-send");
|
||||||
|
parser.addOption(uploadOption);
|
||||||
|
|
||||||
|
const QCommandLineOption authOption("u", "set usename and pass", "username:password");
|
||||||
|
parser.addOption(authOption);
|
||||||
|
|
||||||
|
const QCommandLineOption outputFilenameOption("o", "output filename", "output-file-name");
|
||||||
|
parser.addOption(outputFilenameOption);
|
||||||
|
|
||||||
|
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
|
||||||
|
parser.addOption(domainAddressOption);
|
||||||
|
|
||||||
|
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
|
||||||
|
parser.addOption(listenPortOption);
|
||||||
|
|
||||||
|
if (!parser.parse(QCoreApplication::arguments())) {
|
||||||
|
qCritical() << parser.errorText() << endl;
|
||||||
|
parser.showHelp();
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(helpOption)) {
|
||||||
|
parser.showHelp();
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
_verbose = parser.isSet(verboseOutput);
|
||||||
|
if (!_verbose) {
|
||||||
|
QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
|
||||||
|
|
||||||
|
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, false);
|
||||||
|
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, false);
|
||||||
|
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtWarningMsg, false);
|
||||||
|
|
||||||
|
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtDebugMsg, false);
|
||||||
|
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtInfoMsg, false);
|
||||||
|
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList posArgs = parser.positionalArguments();
|
||||||
|
if (posArgs.size() != 1) {
|
||||||
|
qDebug() << "give remote url argument";
|
||||||
|
parser.showHelp();
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
_url = QUrl(posArgs[0]);
|
||||||
|
if (_url.scheme() != "atp") {
|
||||||
|
qDebug() << "url should start with atp:";
|
||||||
|
parser.showHelp();
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
int domainPort = 40103;
|
||||||
|
if (_url.port() != -1) {
|
||||||
|
domainPort = _url.port();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(outputFilenameOption)) {
|
||||||
|
_localOutputFile = parser.value(outputFilenameOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(uploadOption)) {
|
||||||
|
_localUploadFile = parser.value(uploadOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(authOption)) {
|
||||||
|
QStringList pieces = parser.value(authOption).split(":");
|
||||||
|
if (pieces.size() != 2) {
|
||||||
|
qDebug() << "-u should be followed by username:password";
|
||||||
|
parser.showHelp();
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
_username = pieces[0];
|
||||||
|
_password = pieces[1];
|
||||||
|
_waitingForLogin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(listenPortOption)) {
|
||||||
|
_listenPort = parser.value(listenPortOption).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
_domainServerAddress = QString("127.0.0.1") + ":" + QString::number(domainPort);
|
||||||
|
if (parser.isSet(domainAddressOption)) {
|
||||||
|
_domainServerAddress = parser.value(domainAddressOption);
|
||||||
|
} else if (!_url.host().isEmpty()) {
|
||||||
|
QUrl domainURL;
|
||||||
|
domainURL.setScheme("hifi");
|
||||||
|
domainURL.setHost(_url.host());
|
||||||
|
_domainServerAddress = domainURL.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Setting::init();
|
||||||
|
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||||
|
|
||||||
|
DependencyManager::set<AccountManager>([&]{ return QString(HIGH_FIDELITY_ATP_CLIENT_USER_AGENT); });
|
||||||
|
DependencyManager::set<AddressManager>();
|
||||||
|
DependencyManager::set<NodeList>(NodeType::Agent, _listenPort);
|
||||||
|
|
||||||
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
accountManager->setIsAgent(true);
|
||||||
|
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||||
|
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
// setup a timer for domain-server check ins
|
||||||
|
_domainCheckInTimer = new QTimer(nodeList.data());
|
||||||
|
connect(_domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
|
||||||
|
_domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||||
|
|
||||||
|
// start the nodeThread so its event loop is running
|
||||||
|
// (must happen after the checkin timer is created with the nodelist as it's parent)
|
||||||
|
nodeList->startThread();
|
||||||
|
|
||||||
|
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||||
|
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||||
|
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPClientApp::domainConnectionRefused);
|
||||||
|
|
||||||
|
connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPClientApp::nodeAdded);
|
||||||
|
connect(nodeList.data(), &NodeList::nodeKilled, this, &ATPClientApp::nodeKilled);
|
||||||
|
connect(nodeList.data(), &NodeList::nodeActivated, this, &ATPClientApp::nodeActivated);
|
||||||
|
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ATPClientApp::notifyPacketVersionMismatch);
|
||||||
|
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||||
|
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
QString username = accountManager->getAccountInfo().getUsername();
|
||||||
|
qDebug() << "cached username is" << username << ", isLoggedIn =" << accountManager->isLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_username.isEmpty()) {
|
||||||
|
|
||||||
|
connect(accountManager.data(), &AccountManager::newKeypair, this, [&](){
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "new keypair has been created.";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(accountManager.data(), &AccountManager::loginComplete, this, [&](){
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "login successful";
|
||||||
|
}
|
||||||
|
_waitingForLogin = false;
|
||||||
|
go();
|
||||||
|
});
|
||||||
|
connect(accountManager.data(), &AccountManager::loginFailed, this, [&](){
|
||||||
|
qDebug() << "login failed.";
|
||||||
|
_waitingForLogin = false;
|
||||||
|
go();
|
||||||
|
});
|
||||||
|
accountManager->requestAccessToken(_username, _password);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto assetClient = DependencyManager::set<AssetClient>();
|
||||||
|
assetClient->init();
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "domain-server address is" << _domainServerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyManager::get<AddressManager>()->handleLookupString(_domainServerAddress, false);
|
||||||
|
|
||||||
|
QTimer* _timeoutTimer = new QTimer(this);
|
||||||
|
_timeoutTimer->setSingleShot(true);
|
||||||
|
connect(_timeoutTimer, &QTimer::timeout, this, &ATPClientApp::timedOut);
|
||||||
|
_timeoutTimer->start(TIMEOUT_MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ATPClientApp::~ATPClientApp() {
|
||||||
|
if (_domainCheckInTimer) {
|
||||||
|
QMetaObject::invokeMethod(_domainCheckInTimer, "deleteLater", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
if (_timeoutTimer) {
|
||||||
|
QMetaObject::invokeMethod(_timeoutTimer, "deleteLater", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||||
|
// this is non-fatal if we are trying to get an authenticated connection -- it will be retried.
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "domainConnectionRefused";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::domainChanged(const QString& domainHostname) {
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "domainChanged";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::nodeAdded(SharedNodePointer node) {
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "node added: " << node->getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::nodeActivated(SharedNodePointer node) {
|
||||||
|
if (!_waitingForLogin && node->getType() == NodeType::AssetServer) {
|
||||||
|
_waitingForNode = false;
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::go() {
|
||||||
|
if (_waitingForLogin || _waitingForNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto path = _url.path();
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "path is " << path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_localUploadFile.isEmpty()) {
|
||||||
|
uploadAsset();
|
||||||
|
} else if (path == "/") {
|
||||||
|
listAssets();
|
||||||
|
} else {
|
||||||
|
lookupAsset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::nodeKilled(SharedNodePointer node) {
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "nodeKilled" << node->getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::timedOut() {
|
||||||
|
finish(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::notifyPacketVersionMismatch() {
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "packet version mismatch";
|
||||||
|
}
|
||||||
|
finish(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::uploadAsset() {
|
||||||
|
auto path = _url.path();
|
||||||
|
if (path == "/") {
|
||||||
|
qDebug() << "cannot upload to path of /";
|
||||||
|
QCoreApplication::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto upload = DependencyManager::get<AssetClient>()->createUpload(_localUploadFile);
|
||||||
|
QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable {
|
||||||
|
if (upload->getError() != AssetUpload::NoError) {
|
||||||
|
qDebug() << "upload failed: " << upload->getErrorString();
|
||||||
|
} else {
|
||||||
|
setMapping(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
upload->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
upload->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::setMapping(QString hash) {
|
||||||
|
auto path = _url.path();
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
auto request = assetClient->createSetMappingRequest(path, hash);
|
||||||
|
|
||||||
|
connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable {
|
||||||
|
if (request->getError() != SetMappingRequest::NoError) {
|
||||||
|
qDebug() << "upload succeeded, but couldn't set mapping: " << request->getErrorString();
|
||||||
|
} else if (_verbose) {
|
||||||
|
qDebug() << "mapping set.";
|
||||||
|
}
|
||||||
|
request->deleteLater();
|
||||||
|
finish(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
request->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::listAssets() {
|
||||||
|
auto request = DependencyManager::get<AssetClient>()->createGetAllMappingsRequest();
|
||||||
|
QObject::connect(request, &GetAllMappingsRequest::finished, this, [=](GetAllMappingsRequest* request) mutable {
|
||||||
|
auto result = request->getError();
|
||||||
|
if (result == GetAllMappingsRequest::NotFound) {
|
||||||
|
qDebug() << "not found: " << request->getErrorString();
|
||||||
|
} else if (result == GetAllMappingsRequest::NoError) {
|
||||||
|
auto mappings = request->getMappings();
|
||||||
|
for (auto& kv : mappings ) {
|
||||||
|
qDebug() << kv.first << kv.second;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString();
|
||||||
|
}
|
||||||
|
request->deleteLater();
|
||||||
|
finish(0);
|
||||||
|
});
|
||||||
|
request->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::lookupAsset() {
|
||||||
|
auto path = _url.path();
|
||||||
|
auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(path);
|
||||||
|
QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
|
||||||
|
auto result = request->getError();
|
||||||
|
if (result == GetMappingRequest::NotFound) {
|
||||||
|
qDebug() << "not found: " << request->getErrorString();
|
||||||
|
} else if (result == GetMappingRequest::NoError) {
|
||||||
|
qDebug() << "found, hash is " << request->getHash();
|
||||||
|
download(request->getHash());
|
||||||
|
} else {
|
||||||
|
qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString();
|
||||||
|
}
|
||||||
|
request->deleteLater();
|
||||||
|
});
|
||||||
|
request->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::download(AssetHash hash) {
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
auto assetRequest = new AssetRequest(hash);
|
||||||
|
|
||||||
|
connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) mutable {
|
||||||
|
Q_ASSERT(request->getState() == AssetRequest::Finished);
|
||||||
|
|
||||||
|
if (request->getError() == AssetRequest::Error::NoError) {
|
||||||
|
QString data = QString::fromUtf8(request->getData());
|
||||||
|
if (_localOutputFile == "" || _localOutputFile == "-") {
|
||||||
|
QTextStream cout(stdout);
|
||||||
|
cout << data;
|
||||||
|
} else {
|
||||||
|
QFile outputHandle(_localOutputFile);
|
||||||
|
if (outputHandle.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream( &outputHandle );
|
||||||
|
stream << data;
|
||||||
|
} else {
|
||||||
|
qDebug() << "couldn't open output file:" << _localOutputFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
finish(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
assetRequest->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATPClientApp::finish(int exitCode) {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
||||||
|
nodeList->getDomainHandler().disconnect();
|
||||||
|
nodeList->setIsShuttingDown(true);
|
||||||
|
|
||||||
|
// tell the packet receiver we're shutting down, so it can drop packets
|
||||||
|
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||||
|
|
||||||
|
// remove the NodeList from the DependencyManager
|
||||||
|
DependencyManager::destroy<NodeList>();
|
||||||
|
|
||||||
|
QCoreApplication::exit(exitCode);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// ATPGetApp.h
|
// ATPClientApp.h
|
||||||
// tools/atp-get/src
|
// tools/atp-client/src
|
||||||
//
|
//
|
||||||
// Created by Seth Alves on 2017-3-15
|
// Created by Seth Alves on 2017-3-15
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
@ -10,8 +10,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#ifndef hifi_ATPGetApp_h
|
#ifndef hifi_ATPClientApp_h
|
||||||
#define hifi_ATPGetApp_h
|
#define hifi_ATPClientApp_h
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <udt/Constants.h>
|
#include <udt/Constants.h>
|
||||||
|
@ -23,11 +23,11 @@
|
||||||
#include <MappingRequest.h>
|
#include <MappingRequest.h>
|
||||||
|
|
||||||
|
|
||||||
class ATPGetApp : public QCoreApplication {
|
class ATPClientApp : public QCoreApplication {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ATPGetApp(int argc, char* argv[]);
|
ATPClientApp(int argc, char* argv[]);
|
||||||
~ATPGetApp();
|
~ATPClientApp();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo);
|
void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo);
|
||||||
|
@ -38,15 +38,33 @@ private slots:
|
||||||
void notifyPacketVersionMismatch();
|
void notifyPacketVersionMismatch();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void go();
|
||||||
NodeList* _nodeList;
|
NodeList* _nodeList;
|
||||||
void timedOut();
|
void timedOut();
|
||||||
void lookup();
|
void uploadAsset();
|
||||||
|
void setMapping(QString hash);
|
||||||
|
void lookupAsset();
|
||||||
|
void listAssets();
|
||||||
void download(AssetHash hash);
|
void download(AssetHash hash);
|
||||||
void finish(int exitCode);
|
void finish(int exitCode);
|
||||||
bool _verbose;
|
bool _verbose;
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
QString _localOutputFile;
|
QString _localOutputFile;
|
||||||
|
QString _localUploadFile;
|
||||||
|
|
||||||
|
int _listenPort { INVALID_PORT };
|
||||||
|
|
||||||
|
QString _domainServerAddress;
|
||||||
|
|
||||||
|
QString _username;
|
||||||
|
QString _password;
|
||||||
|
|
||||||
|
bool _waitingForLogin { false };
|
||||||
|
bool _waitingForNode { true };
|
||||||
|
|
||||||
|
QTimer* _domainCheckInTimer { nullptr };
|
||||||
|
QTimer* _timeoutTimer { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ATPGetApp_h
|
#endif // hifi_ATPClientApp_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// main.cpp
|
// main.cpp
|
||||||
// tools/atp-get/src
|
// tools/atp-client/src
|
||||||
//
|
//
|
||||||
// Created by Seth Alves on 2017-3-15
|
// Created by Seth Alves on 2017-3-15
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
#include <BuildInfo.h>
|
#include <BuildInfo.h>
|
||||||
|
|
||||||
#include "ATPGetApp.h"
|
#include "ATPClientApp.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ int main(int argc, char * argv[]) {
|
||||||
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
|
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
|
||||||
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
|
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
|
||||||
|
|
||||||
ATPGetApp app(argc, argv);
|
ATPClientApp app(argc, argv);
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
|
@ -1,255 +0,0 @@
|
||||||
//
|
|
||||||
// ATPGetApp.cpp
|
|
||||||
// tools/atp-get/src
|
|
||||||
//
|
|
||||||
// Created by Seth Alves on 2017-3-15
|
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <QDataStream>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QCommandLineParser>
|
|
||||||
#include <NetworkLogging.h>
|
|
||||||
#include <SharedLogging.h>
|
|
||||||
#include <AddressManager.h>
|
|
||||||
#include <DependencyManager.h>
|
|
||||||
#include <SettingHandle.h>
|
|
||||||
|
|
||||||
#include "ATPGetApp.h"
|
|
||||||
|
|
||||||
ATPGetApp::ATPGetApp(int argc, char* argv[]) :
|
|
||||||
QCoreApplication(argc, argv)
|
|
||||||
{
|
|
||||||
// parse command-line
|
|
||||||
QCommandLineParser parser;
|
|
||||||
parser.setApplicationDescription("High Fidelity ATP-Get");
|
|
||||||
|
|
||||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
|
||||||
|
|
||||||
const QCommandLineOption verboseOutput("v", "verbose output");
|
|
||||||
parser.addOption(verboseOutput);
|
|
||||||
|
|
||||||
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
|
|
||||||
parser.addOption(domainAddressOption);
|
|
||||||
|
|
||||||
const QCommandLineOption cacheSTUNOption("s", "cache stun-server response");
|
|
||||||
parser.addOption(cacheSTUNOption);
|
|
||||||
|
|
||||||
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
|
|
||||||
parser.addOption(listenPortOption);
|
|
||||||
|
|
||||||
|
|
||||||
if (!parser.parse(QCoreApplication::arguments())) {
|
|
||||||
qCritical() << parser.errorText() << endl;
|
|
||||||
parser.showHelp();
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser.isSet(helpOption)) {
|
|
||||||
parser.showHelp();
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
_verbose = parser.isSet(verboseOutput);
|
|
||||||
if (!_verbose) {
|
|
||||||
QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
|
|
||||||
|
|
||||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, false);
|
|
||||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, false);
|
|
||||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtWarningMsg, false);
|
|
||||||
|
|
||||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtDebugMsg, false);
|
|
||||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtInfoMsg, false);
|
|
||||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QStringList filenames = parser.positionalArguments();
|
|
||||||
if (filenames.empty() || filenames.size() > 2) {
|
|
||||||
qDebug() << "give remote url and optional local filename as arguments";
|
|
||||||
parser.showHelp();
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
_url = QUrl(filenames[0]);
|
|
||||||
if (_url.scheme() != "atp") {
|
|
||||||
qDebug() << "url should start with atp:";
|
|
||||||
parser.showHelp();
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filenames.size() == 2) {
|
|
||||||
_localOutputFile = filenames[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
QString domainServerAddress = "127.0.0.1:40103";
|
|
||||||
if (parser.isSet(domainAddressOption)) {
|
|
||||||
domainServerAddress = parser.value(domainAddressOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_verbose) {
|
|
||||||
qDebug() << "domain-server address is" << domainServerAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
int listenPort = INVALID_PORT;
|
|
||||||
if (parser.isSet(listenPortOption)) {
|
|
||||||
listenPort = parser.value(listenPortOption).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
Setting::init();
|
|
||||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
|
||||||
|
|
||||||
DependencyManager::set<AccountManager>([&]{ return QString("Mozilla/5.0 (HighFidelityATPGet)"); });
|
|
||||||
DependencyManager::set<AddressManager>();
|
|
||||||
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
|
||||||
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
nodeList->startThread();
|
|
||||||
|
|
||||||
// setup a timer for domain-server check ins
|
|
||||||
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
|
|
||||||
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
|
|
||||||
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
|
||||||
|
|
||||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
|
||||||
|
|
||||||
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
|
||||||
// connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
|
|
||||||
// connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
|
||||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPGetApp::domainConnectionRefused);
|
|
||||||
|
|
||||||
connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPGetApp::nodeAdded);
|
|
||||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &ATPGetApp::nodeKilled);
|
|
||||||
connect(nodeList.data(), &NodeList::nodeActivated, this, &ATPGetApp::nodeActivated);
|
|
||||||
// connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
|
|
||||||
// connect(nodeList.data(), &NodeList::uuidChanged, this, &ATPGetApp::setSessionUUID);
|
|
||||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ATPGetApp::notifyPacketVersionMismatch);
|
|
||||||
|
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
|
||||||
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
|
||||||
|
|
||||||
DependencyManager::get<AddressManager>()->handleLookupString(domainServerAddress, false);
|
|
||||||
|
|
||||||
auto assetClient = DependencyManager::set<AssetClient>();
|
|
||||||
assetClient->init();
|
|
||||||
|
|
||||||
QTimer* doTimer = new QTimer(this);
|
|
||||||
doTimer->setSingleShot(true);
|
|
||||||
connect(doTimer, &QTimer::timeout, this, &ATPGetApp::timedOut);
|
|
||||||
doTimer->start(4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
ATPGetApp::~ATPGetApp() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ATPGetApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
|
||||||
qDebug() << "domainConnectionRefused";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::domainChanged(const QString& domainHostname) {
|
|
||||||
if (_verbose) {
|
|
||||||
qDebug() << "domainChanged";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::nodeAdded(SharedNodePointer node) {
|
|
||||||
if (_verbose) {
|
|
||||||
qDebug() << "node added: " << node->getType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::nodeActivated(SharedNodePointer node) {
|
|
||||||
if (node->getType() == NodeType::AssetServer) {
|
|
||||||
lookup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::nodeKilled(SharedNodePointer node) {
|
|
||||||
qDebug() << "nodeKilled";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::timedOut() {
|
|
||||||
finish(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::notifyPacketVersionMismatch() {
|
|
||||||
if (_verbose) {
|
|
||||||
qDebug() << "packet version mismatch";
|
|
||||||
}
|
|
||||||
finish(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::lookup() {
|
|
||||||
|
|
||||||
auto path = _url.path();
|
|
||||||
qDebug() << "path is " << path;
|
|
||||||
|
|
||||||
auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(path);
|
|
||||||
QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
|
|
||||||
auto result = request->getError();
|
|
||||||
if (result == GetMappingRequest::NotFound) {
|
|
||||||
qDebug() << "not found";
|
|
||||||
} else if (result == GetMappingRequest::NoError) {
|
|
||||||
qDebug() << "found, hash is " << request->getHash();
|
|
||||||
download(request->getHash());
|
|
||||||
} else {
|
|
||||||
qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString();
|
|
||||||
}
|
|
||||||
request->deleteLater();
|
|
||||||
});
|
|
||||||
request->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::download(AssetHash hash) {
|
|
||||||
auto assetClient = DependencyManager::get<AssetClient>();
|
|
||||||
auto assetRequest = new AssetRequest(hash);
|
|
||||||
|
|
||||||
connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) mutable {
|
|
||||||
Q_ASSERT(request->getState() == AssetRequest::Finished);
|
|
||||||
|
|
||||||
if (request->getError() == AssetRequest::Error::NoError) {
|
|
||||||
QString data = QString::fromUtf8(request->getData());
|
|
||||||
if (_localOutputFile == "") {
|
|
||||||
QTextStream cout(stdout);
|
|
||||||
cout << data;
|
|
||||||
} else {
|
|
||||||
QFile outputHandle(_localOutputFile);
|
|
||||||
if (outputHandle.open(QIODevice::ReadWrite)) {
|
|
||||||
QTextStream stream( &outputHandle );
|
|
||||||
stream << data;
|
|
||||||
} else {
|
|
||||||
qDebug() << "couldn't open output file:" << _localOutputFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request->deleteLater();
|
|
||||||
finish(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
assetRequest->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ATPGetApp::finish(int exitCode) {
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
|
|
||||||
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
|
||||||
nodeList->getDomainHandler().disconnect();
|
|
||||||
nodeList->setIsShuttingDown(true);
|
|
||||||
|
|
||||||
// tell the packet receiver we're shutting down, so it can drop packets
|
|
||||||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
|
||||||
|
|
||||||
// remove the NodeList from the DependencyManager
|
|
||||||
DependencyManager::destroy<NodeList>();
|
|
||||||
|
|
||||||
QCoreApplication::exit(exitCode);
|
|
||||||
}
|
|
|
@ -354,8 +354,8 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
|
||||||
FbxFileTexture* fileTexture = property.GetSrcObject<FbxFileTexture>(j);
|
FbxFileTexture* fileTexture = property.GetSrcObject<FbxFileTexture>(j);
|
||||||
|
|
||||||
// use QFileInfo to easily split up the existing texture filename into its components
|
// use QFileInfo to easily split up the existing texture filename into its components
|
||||||
QString fbxFileName { fileTexture->GetFileName() };
|
QString fbxTextureFileName { fileTexture->GetFileName() };
|
||||||
QFileInfo textureFileInfo { fbxFileName.replace("\\", "/") };
|
QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") };
|
||||||
|
|
||||||
// make sure this texture points to something and isn't one we've already re-mapped
|
// make sure this texture points to something and isn't one we've already re-mapped
|
||||||
if (!textureFileInfo.filePath().isEmpty()
|
if (!textureFileInfo.filePath().isEmpty()
|
||||||
|
@ -372,6 +372,9 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
|
||||||
qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName()
|
qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName()
|
||||||
<< "to" << bakedTextureFilePath;
|
<< "to" << bakedTextureFilePath;
|
||||||
|
|
||||||
|
// figure out the URL to this texture, embedded or external
|
||||||
|
auto urlToTexture = getTextureURL(textureFileInfo, fileTexture);
|
||||||
|
|
||||||
// write the new filename into the FBX scene
|
// write the new filename into the FBX scene
|
||||||
fileTexture->SetFileName(bakedTextureFilePath.toLocal8Bit());
|
fileTexture->SetFileName(bakedTextureFilePath.toLocal8Bit());
|
||||||
|
|
||||||
|
@ -379,9 +382,6 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
|
||||||
// be right beside the FBX
|
// be right beside the FBX
|
||||||
fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData());
|
fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData());
|
||||||
|
|
||||||
// figure out the URL to this texture, embedded or external
|
|
||||||
auto urlToTexture = getTextureURL(textureFileInfo, fileTexture);
|
|
||||||
|
|
||||||
if (!_bakingTextures.contains(urlToTexture)) {
|
if (!_bakingTextures.contains(urlToTexture)) {
|
||||||
// bake this texture asynchronously
|
// bake this texture asynchronously
|
||||||
bakeTexture(urlToTexture, textureType, _uniqueOutputPath + BAKED_OUTPUT_SUBFOLDER);
|
bakeTexture(urlToTexture, textureType, _uniqueOutputPath + BAKED_OUTPUT_SUBFOLDER);
|
||||||
|
|
Loading…
Reference in a new issue