diff --git a/.eslintrc.js b/.eslintrc.js
index 82dfe9e9bd..6183fa8aec 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -72,6 +72,6 @@ module.exports = {
"spaced-comment": ["error", "always", {
"line": { "markers": ["/"] }
}],
- "space-before-function-paren": ["error", {"anonymous": "always", "named": "never"}]
+ "space-before-function-paren": ["error", {"anonymous": "ignore", "named": "never"}]
}
};
diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 8f752e70d0..eabb4955d9 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -100,6 +100,63 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f;
+const int IEEE754_MANT_BITS = 23;
+const int IEEE754_EXPN_BIAS = 127;
+
+//
+// for x > 0.0f, returns log2(x)
+// for x <= 0.0f, returns large negative value
+//
+// abs |error| < 8e-3, smooth (exact for x=2^N) for NPOLY=3
+// abs |error| < 2e-4, smooth (exact for x=2^N) for NPOLY=5
+// rel |error| < 0.4 from precision loss very close to 1.0f
+//
+static inline float fastlog2(float x) {
+
+ union { float f; int32_t i; } mant, bits = { x };
+
+ // split into mantissa and exponent
+ mant.i = (bits.i & ((1 << IEEE754_MANT_BITS) - 1)) | (IEEE754_EXPN_BIAS << IEEE754_MANT_BITS);
+ int32_t expn = (bits.i >> IEEE754_MANT_BITS) - IEEE754_EXPN_BIAS;
+
+ mant.f -= 1.0f;
+
+ // polynomial for log2(1+x) over x=[0,1]
+ //x = (-0.346555386f * mant.f + 1.346555386f) * mant.f;
+ x = (((-0.0821307180f * mant.f + 0.321188984f) * mant.f - 0.677784014f) * mant.f + 1.43872575f) * mant.f;
+
+ return x + expn;
+}
+
+//
+// for -126 <= x < 128, returns exp2(x)
+//
+// rel |error| < 3e-3, smooth (exact for x=N) for NPOLY=3
+// rel |error| < 9e-6, smooth (exact for x=N) for NPOLY=5
+//
+static inline float fastexp2(float x) {
+
+ union { float f; int32_t i; } xi;
+
+ // bias such that x > 0
+ x += IEEE754_EXPN_BIAS;
+ //x = MAX(x, 1.0f);
+ //x = MIN(x, 254.9999f);
+
+ // split into integer and fraction
+ xi.i = (int32_t)x;
+ x -= xi.i;
+
+ // construct exp2(xi) as a float
+ xi.i <<= IEEE754_MANT_BITS;
+
+ // polynomial for exp2(x) over x=[0,1]
+ //x = (0.339766028f * x + 0.660233972f) * x + 1.0f;
+ x = (((0.0135557472f * x + 0.0520323690f) * x + 0.241379763f) * x + 0.693032121f) * x + 1.0f;
+
+ return x * xi.f;
+}
+
float AudioMixer::gainForSource(const PositionalAudioStream& streamToAdd,
const AvatarAudioStream& listeningNodeStream, const glm::vec3& relativePosition, bool isEcho) {
float gain = 1.0f;
@@ -148,7 +205,7 @@ float AudioMixer::gainForSource(const PositionalAudioStream& streamToAdd,
g = (g > 1.0f) ? 1.0f : g;
// calculate the distance coefficient using the distance to this node
- float distanceCoefficient = exp2f(log2f(g) * log2f(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE));
+ float distanceCoefficient = fastexp2(fastlog2(g) * fastlog2(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE));
// multiply the current attenuation coefficient by the distance coefficient
gain *= distanceCoefficient;
diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index d763d1abe7..48ffc2fdbc 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -1063,6 +1063,12 @@ void OctreeServer::readConfiguration() {
_wantBackup = !noBackup;
qDebug() << "wantBackup=" << _wantBackup;
+ if (!readOptionString("backupDirectoryPath", settingsSectionObject, _backupDirectoryPath)) {
+ _backupDirectoryPath = "";
+ }
+
+ qDebug() << "backupDirectoryPath=" << _backupDirectoryPath;
+
readOptionBool(QString("persistFileDownload"), settingsSectionObject, _persistFileDownload);
qDebug() << "persistFileDownload=" << _persistFileDownload;
@@ -1160,25 +1166,25 @@ void OctreeServer::domainSettingsRequestComplete() {
// If persist filename does not exist, let's see if there is one beside the application binary
// If there is, let's copy it over to our target persist directory
QDir persistPath { _persistFilePath };
- QString absoluteFilePath = persistPath.absolutePath();
+ QString persistAbsoluteFilePath = persistPath.absolutePath();
if (persistPath.isRelative()) {
// if the domain settings passed us a relative path, make an absolute path that is relative to the
// default data directory
- absoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath);
+ persistAbsoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath);
}
static const QString ENTITY_PERSIST_EXTENSION = ".json.gz";
// force the persist file to end with .json.gz
- if (!absoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) {
- absoluteFilePath += ENTITY_PERSIST_EXTENSION;
+ if (!persistAbsoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) {
+ persistAbsoluteFilePath += ENTITY_PERSIST_EXTENSION;
} else {
// make sure the casing of .json.gz is correct
- absoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive);
+ persistAbsoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive);
}
- if (!QFile::exists(absoluteFilePath)) {
+ if (!QFile::exists(persistAbsoluteFilePath)) {
qDebug() << "Persist file does not exist, checking for existence of persist file next to application";
static const QString OLD_DEFAULT_PERSIST_FILENAME = "resources/models.json.gz";
@@ -1204,7 +1210,7 @@ void OctreeServer::domainSettingsRequestComplete() {
pathToCopyFrom = oldDefaultPersistPath;
}
- QDir persistFileDirectory { QDir::cleanPath(absoluteFilePath + "/..") };
+ QDir persistFileDirectory { QDir::cleanPath(persistAbsoluteFilePath + "/..") };
if (!persistFileDirectory.exists()) {
qDebug() << "Creating data directory " << persistFileDirectory.absolutePath();
@@ -1212,16 +1218,46 @@ void OctreeServer::domainSettingsRequestComplete() {
}
if (shouldCopy) {
- qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << absoluteFilePath;
+ qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistAbsoluteFilePath;
- QFile::copy(pathToCopyFrom, absoluteFilePath);
+ QFile::copy(pathToCopyFrom, persistAbsoluteFilePath);
} else {
qDebug() << "No existing persist file found";
}
}
+
+ auto persistFileDirectory = QFileInfo(persistAbsoluteFilePath).absolutePath();
+ if (_backupDirectoryPath.isEmpty()) {
+ // Use the persist file's directory to store backups
+ _backupDirectoryPath = persistFileDirectory;
+ } else {
+ // The backup directory has been set.
+ // If relative, make it relative to the entities directory in the application data directory
+ // If absolute, no resolution is necessary
+ QDir backupDirectory { _backupDirectoryPath };
+ QString absoluteBackupDirectory;
+ if (backupDirectory.isRelative()) {
+ absoluteBackupDirectory = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath);
+ absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath();
+ } else {
+ absoluteBackupDirectory = backupDirectory.absolutePath();
+ }
+ backupDirectory = QDir(absoluteBackupDirectory);
+ if (!backupDirectory.exists()) {
+ if (backupDirectory.mkpath(".")) {
+ qDebug() << "Created backup directory";
+ } else {
+ qDebug() << "ERROR creating backup directory, using persist file directory";
+ _backupDirectoryPath = persistFileDirectory;
+ }
+ } else {
+ _backupDirectoryPath = absoluteBackupDirectory;
+ }
+ }
+ qDebug() << "Backups will be stored in: " << _backupDirectoryPath;
// now set up PersistThread
- _persistThread = new OctreePersistThread(_tree, absoluteFilePath, _persistInterval,
+ _persistThread = new OctreePersistThread(_tree, persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval,
_wantBackup, _settings, _debugTimestampNow, _persistAsFileType);
_persistThread->initialize(true);
}
diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h
index d153f31154..35436997a6 100644
--- a/assignment-client/src/octree/OctreeServer.h
+++ b/assignment-client/src/octree/OctreeServer.h
@@ -172,6 +172,7 @@ protected:
QString _persistFilePath;
QString _persistAsFileType;
+ QString _backupDirectoryPath;
int _packetsPerClientPerInterval;
int _packetsTotalPerInterval;
OctreePointer _tree; // this IS a reaveraging tree
diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt
index 930a339d12..1cd4c071f1 100644
--- a/cmake/externals/openvr/CMakeLists.txt
+++ b/cmake/externals/openvr/CMakeLists.txt
@@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
ExternalProject_Add(
${EXTERNAL_NAME}
- URL https://github.com/ValveSoftware/openvr/archive/v0.9.19.zip
- URL_MD5 843f9dde488584d8af1f3ecf2252b4e0
+ URL https://github.com/ValveSoftware/openvr/archive/v1.0.2.zip
+ URL_MD5 0d1cf5f579cf092e33f34759967b7046
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake
index dfa59943d6..e586304503 100755
--- a/cmake/macros/AutoScribeShader.cmake
+++ b/cmake/macros/AutoScribeShader.cmake
@@ -23,13 +23,13 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
#Extract the unique include shader paths
set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
- #message(Hifi for includes ${INCLUDES})
- foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
+ #message(${TARGET_NAME} Hifi for includes ${INCLUDES})
+ foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE})
endforeach()
list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
- #message(ready for includes ${SHADER_INCLUDES_PATHS})
+ #message(ready for includes ${SHADER_INCLUDES_PATHS})
# make the scribe include arguments
set(SCRIBE_INCLUDES)
@@ -77,6 +77,7 @@ endfunction()
macro(AUTOSCRIBE_SHADER_LIB)
+ set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "")
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
foreach(HIFI_LIBRARY ${ARGN})
#if (NOT TARGET ${HIFI_LIBRARY})
@@ -86,7 +87,7 @@ macro(AUTOSCRIBE_SHADER_LIB)
#file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh)
list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src)
endforeach()
- #message(${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
+ #message("${TARGET_NAME} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}")
file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg)
@@ -95,13 +96,14 @@ macro(AUTOSCRIBE_SHADER_LIB)
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
file(MAKE_DIRECTORY ${SHADERS_DIR})
- #message(${SHADER_INCLUDE_FILES})
+ #message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}")
+ set(AUTOSCRIBE_SHADER_SRC "")
foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
endforeach()
- #message(${AUTOSCRIBE_SHADER_SRC})
+ #message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC})
if (WIN32)
source_group("Shaders" FILES ${SHADER_INCLUDE_FILES})
@@ -116,4 +118,4 @@ macro(AUTOSCRIBE_SHADER_LIB)
# Link library shaders, if they exist
include_directories("${SHADERS_DIR}")
-endmacro()
+endmacro()
\ No newline at end of file
diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake
index 26c769c6e6..a10c7c11e6 100644
--- a/cmake/macros/SetupHifiLibrary.cmake
+++ b/cmake/macros/SetupHifiLibrary.cmake
@@ -54,8 +54,9 @@ macro(SETUP_HIFI_LIBRARY)
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
endforeach()
- # Don't make scribed shaders cumulative
+ # Don't make scribed shaders or QT resource files cumulative
set(AUTOSCRIBE_SHADER_LIB_SRC "")
+ set(QT_RESOURCES_FILE "")
target_glm()
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index c888fa301b..c9d7ea77d5 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -1108,6 +1108,14 @@
"default": "models.json.gz",
"advanced": true
},
+ {
+ "name": "backupDirectoryPath",
+ "label": "Entities Backup Directory Path",
+ "help": "The path to the directory to store backups in.
If this path is relative it will be relative to the application data directory.",
+ "placeholder": "",
+ "default": "",
+ "advanced": true
+ },
{
"name": "persistInterval",
"label": "Save Check Interval",
diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml
index 4c937d6139..802038d806 100644
--- a/domain-server/resources/web/settings/index.shtml
+++ b/domain-server/resources/web/settings/index.shtml
@@ -25,7 +25,7 @@
-
+
diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp
index 8f8c8e001c..c827e79223 100644
--- a/domain-server/src/DomainGatekeeper.cpp
+++ b/domain-server/src/DomainGatekeeper.cpp
@@ -182,7 +182,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID);
#ifdef WANT_DEBUG
- qDebug() << "| user-permissions: user is in group:" << groupID << " rank:"
+ qDebug() << "| user-permissions: user " << verifiedUsername << "is in group:" << groupID << " rank:"
<< rank.name << "so:" << userPerms;
#endif
}
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 23e37efaf1..d5f23f7c4e 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -117,9 +117,18 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_settingsManager.apiRefreshGroupInformation();
setupNodeListAndAssignments();
+
+ if (_type == MetaverseDomain) {
+ // if we have a metaverse domain, we'll need an access token to heartbeat handle auto-networking
+ resetAccountManagerAccessToken();
+ }
+
setupAutomaticNetworking();
- if (!getID().isNull()) {
+
+ if (!getID().isNull() && _type != NonMetaverse) {
+ // setup periodic heartbeats to metaverse API
setupHeartbeatToMetaverse();
+
// send the first heartbeat immediately
sendHeartbeatToMetaverse();
}
@@ -301,16 +310,22 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) {
// store the new ID and auto networking setting on disk
_settingsManager.persistToFile();
- // change our domain ID immediately
- DependencyManager::get()->setSessionUUID(QUuid { id });
-
// store the new token to the account info
auto accountManager = DependencyManager::get();
accountManager->setTemporaryDomain(id, key);
+ // change our domain ID immediately
+ DependencyManager::get()->setSessionUUID(QUuid { id });
+
+ // change our type to reflect that we are a temporary domain now
+ _type = MetaverseTemporaryDomain;
+
// update our heartbeats to use the correct id
setupICEHeartbeatForFullNetworking();
setupHeartbeatToMetaverse();
+
+ // if we have a current ICE server address, update it in the API for the new temporary domain
+ sendICEServerAddressToMetaverseAPI();
} else {
qWarning() << "There were problems parsing the API response containing a temporary domain name. Please try again"
<< "via domain-server relaunch or from the domain-server settings.";
@@ -394,6 +409,16 @@ void DomainServer::setupNodeListAndAssignments() {
const QVariant* idValueVariant = valueForKeyPath(settingsMap, METAVERSE_DOMAIN_ID_KEY_PATH);
if (idValueVariant) {
nodeList->setSessionUUID(idValueVariant->toString());
+
+ // if we have an ID, we'll assume we're a metaverse domain
+ // now see if we think we're a temp domain (we have an API key) or a full domain
+ const auto& temporaryDomainKey = DependencyManager::get()->getTemporaryDomainKey(getID());
+ if (temporaryDomainKey.isEmpty()) {
+ _type = MetaverseDomain;
+ } else {
+ _type = MetaverseTemporaryDomain;
+ }
+
} else {
nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID
}
@@ -477,42 +502,46 @@ bool DomainServer::resetAccountManagerAccessToken() {
}
void DomainServer::setupAutomaticNetworking() {
- qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting;
-
- resetAccountManagerAccessToken();
_automaticNetworkingSetting =
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
- auto nodeList = DependencyManager::get();
- const QUuid& domainID = getID();
+ qDebug() << "Configuring automatic networking in domain-server as" << _automaticNetworkingSetting;
- if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
- setupICEHeartbeatForFullNetworking();
- }
+ if (_automaticNetworkingSetting != DISABLED_AUTOMATIC_NETWORKING_VALUE) {
+ const QUuid& domainID = getID();
- if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
- _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
+ if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
+ setupICEHeartbeatForFullNetworking();
+ }
- if (!domainID.isNull()) {
- qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID"
- << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
+ if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
+ _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
- if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
- // send any public socket changes to the data server so nodes can find us at our new IP
- connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged,
- this, &DomainServer::performIPAddressUpdate);
+ if (!domainID.isNull()) {
+ qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID"
+ << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
- // have the LNL enable public socket updating via STUN
- nodeList->startSTUNPublicSocketUpdate();
+ if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
+
+ auto nodeList = DependencyManager::get();
+
+ // send any public socket changes to the data server so nodes can find us at our new IP
+ connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged,
+ this, &DomainServer::performIPAddressUpdate);
+
+ // have the LNL enable public socket updating via STUN
+ nodeList->startSTUNPublicSocketUpdate();
+ }
+ } else {
+ qDebug() << "Cannot enable domain-server automatic networking without a domain ID."
+ << "Please add an ID to your config file or via the web interface.";
+
+ return;
}
- } else {
- qDebug() << "Cannot enable domain-server automatic networking without a domain ID."
- << "Please add an ID to your config file or via the web interface.";
-
- return;
}
}
+
}
void DomainServer::setupHeartbeatToMetaverse() {
@@ -1139,42 +1168,45 @@ void DomainServer::handleMetaverseHeartbeatError(QNetworkReply& requestReply) {
return;
}
- // check if we need to force a new temporary domain name
- switch (requestReply.error()) {
- // if we have a temporary domain with a bad token, we get a 401
- case QNetworkReply::NetworkError::AuthenticationRequiredError: {
- static const QString DATA_KEY = "data";
- static const QString TOKEN_KEY = "api_key";
+ // only attempt to grab a new temporary name if we're already a temporary domain server
+ if (_type == MetaverseTemporaryDomain) {
+ // check if we need to force a new temporary domain name
+ switch (requestReply.error()) {
+ // if we have a temporary domain with a bad token, we get a 401
+ case QNetworkReply::NetworkError::AuthenticationRequiredError: {
+ static const QString DATA_KEY = "data";
+ static const QString TOKEN_KEY = "api_key";
- QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
- auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY];
+ QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
+ auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY];
- if (!tokenFailure.isNull()) {
- qWarning() << "Temporary domain name lacks a valid API key, and is being reset.";
+ if (!tokenFailure.isNull()) {
+ qWarning() << "Temporary domain name lacks a valid API key, and is being reset.";
+ }
+ break;
}
- break;
+ // if the domain does not (or no longer) exists, we get a 404
+ case QNetworkReply::NetworkError::ContentNotFoundError:
+ qWarning() << "Domain not found, getting a new temporary domain.";
+ break;
+ // otherwise, we erred on something else, and should not force a temporary domain
+ default:
+ return;
}
- // if the domain does not (or no longer) exists, we get a 404
- case QNetworkReply::NetworkError::ContentNotFoundError:
- qWarning() << "Domain not found, getting a new temporary domain.";
- break;
- // otherwise, we erred on something else, and should not force a temporary domain
- default:
- return;
- }
- // halt heartbeats until we have a token
- _metaverseHeartbeatTimer->deleteLater();
- _metaverseHeartbeatTimer = nullptr;
+ // halt heartbeats until we have a token
+ _metaverseHeartbeatTimer->deleteLater();
+ _metaverseHeartbeatTimer = nullptr;
- // give up eventually to avoid flooding traffic
- static const int MAX_ATTEMPTS = 5;
- static int attempt = 0;
- if (++attempt < MAX_ATTEMPTS) {
- // get a new temporary name and token
- getTemporaryName(true);
- } else {
- qWarning() << "Already attempted too many temporary domain requests. Please set a domain ID manually or restart.";
+ // give up eventually to avoid flooding traffic
+ static const int MAX_ATTEMPTS = 5;
+ static int attempt = 0;
+ if (++attempt < MAX_ATTEMPTS) {
+ // get a new temporary name and token
+ getTemporaryName(true);
+ } else {
+ qWarning() << "Already attempted too many temporary domain requests. Please set a domain ID manually or restart.";
+ }
}
}
@@ -1201,7 +1233,10 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
callbackParameters.errorCallbackReceiver = this;
callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate";
- qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" << _iceServerSocket.getAddress().toString();
+ static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
+ ("Updating ice-server address in High Fidelity Metaverse API to [^ \n]+");
+ qDebug() << "Updating ice-server address in High Fidelity Metaverse API to"
+ << _iceServerSocket.getAddress().toString();
static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address";
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 4004333789..06b3ed2c0a 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -42,6 +42,12 @@ public:
DomainServer(int argc, char* argv[]);
~DomainServer();
+ enum DomainType {
+ NonMetaverse,
+ MetaverseDomain,
+ MetaverseTemporaryDomain
+ };
+
static int const EXIT_CODE_REBOOT;
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
@@ -195,6 +201,8 @@ private:
int _numHeartbeatDenials { 0 };
bool _connectedToICEServer { false };
+ DomainType _type { DomainType::NonMetaverse };
+
friend class DomainGatekeeper;
friend class DomainMetadata;
};
diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index dc49bc6126..c7944bbcad 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -356,7 +356,7 @@ void DomainServerSettingsManager::initializeGroupPermissions(NodePermissionsMap&
if (nameKey.first.toLower() != groupNameLower) {
continue;
}
- QUuid groupID = _groupIDs[groupNameLower];
+ QUuid groupID = _groupIDs[groupNameLower.toLower()];
QUuid rankID = nameKey.second;
GroupRank rank = _groupRanks[groupID][rankID];
if (rank.order == 0) {
@@ -532,9 +532,12 @@ void DomainServerSettingsManager::unpackPermissions() {
// we don't have permissions for one of the standard groups, so we'll add them now
NodePermissionsPointer perms { new NodePermissions(standardKey) };
- // the localhost user is granted all permissions by default
if (standardKey == NodePermissions::standardNameLocalhost) {
+ // the localhost user is granted all permissions by default
perms->setAll(true);
+ } else {
+ // anonymous, logged in, and friend users get connect permissions by default
+ perms->set(NodePermissions::Permission::canConnectToDomain);
}
// add the permissions to the standard map
@@ -1477,14 +1480,14 @@ void DomainServerSettingsManager::apiGetGroupRanksErrorCallback(QNetworkReply& r
void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID) {
if (rankID != QUuid()) {
- _groupMembership[name][groupID] = rankID;
+ _groupMembership[name.toLower()][groupID] = rankID;
} else {
- _groupMembership[name].remove(groupID);
+ _groupMembership[name.toLower()].remove(groupID);
}
}
QUuid DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) {
- const QHash& groupsForName = _groupMembership[name];
+ const QHash& groupsForName = _groupMembership[name.toLower()];
if (groupsForName.contains(groupID)) {
return groupsForName[groupID];
}
@@ -1528,7 +1531,7 @@ void DomainServerSettingsManager::debugDumpGroupsState() {
qDebug() << "_groupIDs:";
foreach (QString groupName, _groupIDs.keys()) {
- qDebug() << "| " << groupName << "==>" << _groupIDs[groupName];
+ qDebug() << "| " << groupName << "==>" << _groupIDs[groupName.toLower()];
}
qDebug() << "_groupNames:";
@@ -1548,7 +1551,7 @@ void DomainServerSettingsManager::debugDumpGroupsState() {
qDebug() << "_groupMembership";
foreach (QString userName, _groupMembership.keys()) {
- QHash& groupsForUser = _groupMembership[userName];
+ QHash& groupsForUser = _groupMembership[userName.toLower()];
QString line = "";
foreach (QUuid groupID, groupsForUser.keys()) {
line += " g=" + groupID.toString() + ",r=" + groupsForUser[groupID].toString();
diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h
index 144589326c..c067377ffc 100644
--- a/domain-server/src/DomainServerSettingsManager.h
+++ b/domain-server/src/DomainServerSettingsManager.h
@@ -84,7 +84,7 @@ public:
QList getBlacklistGroupIDs();
// these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api
- void clearGroupMemberships(const QString& name) { _groupMembership[name].clear(); }
+ void clearGroupMemberships(const QString& name) { _groupMembership[name.toLower()].clear(); }
void recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID);
QUuid isGroupMember(const QString& name, const QUuid& groupID); // returns rank or -1 if not a member
diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json
index 0037e3741d..8baf56684a 100644
--- a/interface/resources/controllers/keyboardMouse.json
+++ b/interface/resources/controllers/keyboardMouse.json
@@ -63,6 +63,19 @@
["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
]
},
+ "when": "Application.CameraFirstPerson",
+ "to": "Actions.Yaw"
+ },
+ { "from": { "makeAxis" : [
+ ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"],
+ ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
+ ]
+ },
+ "when": "Application.CameraThirdPerson",
+ "to": "Actions.Yaw"
+ },
+ { "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] ] },
+ "when": "Application.CameraFSM",
"to": "Actions.Yaw"
},
@@ -81,9 +94,10 @@
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" },
-
- { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" },
- { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" },
+ { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
+ { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
+ { "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
+ { "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" },
diff --git a/interface/resources/controllers/oculus_touch.json b/interface/resources/controllers/oculus_touch.json
index 82f52e50db..c50f7681d9 100644
--- a/interface/resources/controllers/oculus_touch.json
+++ b/interface/resources/controllers/oculus_touch.json
@@ -1,8 +1,13 @@
{
"name": "Oculus Touch to Standard",
"channels": [
- { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb" },
- { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb" },
+ { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb", "peek": true },
+ { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb", "peek": true },
+
+ { "from": "OculusTouch.A", "to": "Standard.A" },
+ { "from": "OculusTouch.B", "to": "Standard.B" },
+ { "from": "OculusTouch.X", "to": "Standard.X" },
+ { "from": "OculusTouch.Y", "to": "Standard.Y" },
{ "from": "OculusTouch.LY", "to": "Standard.LY",
"filters": [
@@ -17,7 +22,11 @@
},
{ "from": "OculusTouch.LT", "to": "Standard.LT" },
{ "from": "OculusTouch.LS", "to": "Standard.LS" },
- { "from": "OculusTouch.LeftGrip", "to": "Standard.LeftGrip" },
+ { "from": "OculusTouch.LeftGrip", "to": "Standard.LTClick",
+ "peek": true,
+ "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
+ },
+ { "from": "OculusTouch.LeftGrip", "to": "Standard.LT" },
{ "from": "OculusTouch.LeftHand", "to": "Standard.LeftHand" },
{ "from": "OculusTouch.RY", "to": "Standard.RY",
@@ -33,7 +42,11 @@
},
{ "from": "OculusTouch.RT", "to": "Standard.RT" },
{ "from": "OculusTouch.RS", "to": "Standard.RS" },
- { "from": "OculusTouch.RightGrip", "to": "Standard.RightGrip" },
+ { "from": "OculusTouch.RightGrip", "to": "Standard.LTClick",
+ "peek": true,
+ "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
+ },
+ { "from": "OculusTouch.RightGrip", "to": "Standard.RT" },
{ "from": "OculusTouch.RightHand", "to": "Standard.RightHand" },
{ "from": "OculusTouch.LeftApplicationMenu", "to": "Standard.Back" },
@@ -54,4 +67,3 @@
]
}
-
diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json
index 5c0ea09939..222357ac9d 100644
--- a/interface/resources/controllers/standard.json
+++ b/interface/resources/controllers/standard.json
@@ -32,9 +32,6 @@
{ "from": "Standard.Back", "to": "Actions.CycleCamera" },
{ "from": "Standard.Start", "to": "Actions.ContextMenu" },
- { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
- { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" },
-
{ "from": "Standard.LT", "to": "Actions.LeftHandClick" },
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json
index fdac70ff33..1234372a7e 100644
--- a/interface/resources/controllers/xbox.json
+++ b/interface/resources/controllers/xbox.json
@@ -15,12 +15,14 @@
{ "from": "GamePad.Back", "to": "Standard.Back" },
{ "from": "GamePad.Start", "to": "Standard.Start" },
-
+
+ { "from": [ "GamePad.DU", "GamePad.DL", "GamePad.DR", "GamePad.DD" ], "to": "Standard.LeftPrimaryThumb", "peek": true },
{ "from": "GamePad.DU", "to": "Standard.DU" },
{ "from": "GamePad.DD", "to": "Standard.DD" },
{ "from": "GamePad.DL", "to": "Standard.DL" },
{ "from": "GamePad.DR", "to": "Standard.DR" },
+ { "from": [ "GamePad.A", "GamePad.B", "GamePad.X", "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true },
{ "from": "GamePad.A", "to": "Standard.A" },
{ "from": "GamePad.B", "to": "Standard.B" },
{ "from": "GamePad.X", "to": "Standard.X" },
diff --git a/interface/resources/images/Default-Sky-9-ambient.jpg b/interface/resources/images/Default-Sky-9-ambient.jpg
new file mode 100644
index 0000000000..8fb383c5e8
Binary files /dev/null and b/interface/resources/images/Default-Sky-9-ambient.jpg differ
diff --git a/interface/resources/images/Default-Sky-9-cubemap.jpg b/interface/resources/images/Default-Sky-9-cubemap.jpg
new file mode 100644
index 0000000000..697fd9aeea
Binary files /dev/null and b/interface/resources/images/Default-Sky-9-cubemap.jpg differ
diff --git a/interface/resources/images/preview.png b/interface/resources/images/preview.png
index faebbfce8f..85a62f98bd 100644
Binary files a/interface/resources/images/preview.png and b/interface/resources/images/preview.png differ
diff --git a/interface/resources/images/steam-sign-in.png b/interface/resources/images/steam-sign-in.png
new file mode 100644
index 0000000000..148e6ab280
Binary files /dev/null and b/interface/resources/images/steam-sign-in.png differ
diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml
index 0f55e77a66..87dcf9368c 100644
--- a/interface/resources/qml/AssetServer.qml
+++ b/interface/resources/qml/AssetServer.qml
@@ -314,6 +314,14 @@ ScrollingWindow {
});
}
+ Timer {
+ id: doUploadTimer
+ property var url
+ property bool isConnected: false
+ interval: 5
+ repeat: false
+ running: false
+ }
property var uploadOpen: false;
Timer {
@@ -367,6 +375,10 @@ ScrollingWindow {
}, dropping);
}
+ function initiateUpload(url) {
+ doUpload(doUploadTimer.url, false);
+ }
+
if (fileUrl) {
doUpload(fileUrl, true);
} else {
@@ -374,12 +386,21 @@ ScrollingWindow {
selectDirectory: false,
dir: currentDirectory
});
+
browser.canceled.connect(function() {
uploadOpen = false;
});
+
browser.selectedFile.connect(function(url) {
currentDirectory = browser.dir;
- doUpload(url, false);
+
+ // Initiate upload from a timer so that file browser dialog can close beforehand.
+ doUploadTimer.url = url;
+ if (!doUploadTimer.isConnected) {
+ doUploadTimer.triggered.connect(function() { initiateUpload(); });
+ doUploadTimer.isConnected = true;
+ }
+ doUploadTimer.start();
});
}
}
@@ -501,14 +522,15 @@ ScrollingWindow {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
- var index = treeView.indexAt(mouse.x, mouse.y);
-
- treeView.selection.setCurrentIndex(index, 0x0002);
-
- contextMenu.currentIndex = index;
- contextMenu.popup();
+ if (!HMD.active) { // Popup only displays properly on desktop
+ var index = treeView.indexAt(mouse.x, mouse.y);
+ treeView.selection.setCurrentIndex(index, 0x0002);
+ contextMenu.currentIndex = index;
+ contextMenu.popup();
+ }
}
}
+
}
HifiControls.ContentSection {
id: uploadSection
diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml
index f75e83e36e..1f84024e15 100644
--- a/interface/resources/qml/LoginDialog.qml
+++ b/interface/resources/qml/LoginDialog.qml
@@ -10,296 +10,70 @@
import Hifi 1.0
import QtQuick 2.4
-import "controls"
-import "styles"
+
+import "controls-uit"
+import "styles-uit"
import "windows"
-ScrollingWindow {
+import "LoginDialog"
+
+ModalWindow {
id: root
HifiConstants { id: hifi }
objectName: "LoginDialog"
- height: loginDialog.implicitHeight
- width: loginDialog.implicitWidth
- // FIXME make movable
- anchors.centerIn: parent
- destroyOnHidden: false
- hideBackground: true
- shown: false
+ implicitWidth: 520
+ implicitHeight: 320
+ destroyOnCloseButton: true
+ destroyOnHidden: true
+ visible: true
+
+ property string iconText: ""
+ property int iconSize: 50
+
+ property string title: ""
+ property int titleWidth: 0
LoginDialog {
id: loginDialog
- implicitWidth: backgroundRectangle.width
- implicitHeight: backgroundRectangle.height
- readonly property int inputWidth: 500
- readonly property int inputHeight: 60
- readonly property int borderWidth: 30
- readonly property int closeMargin: 16
- readonly property real tan30: 0.577 // tan(30°)
- readonly property int inputSpacing: 16
- Rectangle {
- id: backgroundRectangle
- width: loginDialog.inputWidth + loginDialog.borderWidth * 2
- height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2
- radius: loginDialog.closeMargin * 2
- color: "#2c86b1"
- opacity: 0.85
- }
-
- Column {
- id: mainContent
- width: loginDialog.inputWidth
- spacing: loginDialog.inputSpacing
- anchors {
- horizontalCenter: parent.horizontalCenter
- verticalCenter: parent.verticalCenter
- }
-
- Item {
- // Offset content down a little
- width: loginDialog.inputWidth
- height: loginDialog.closeMargin
- }
-
- Rectangle {
- width: loginDialog.inputWidth
- height: loginDialog.inputHeight
- radius: height / 2
- color: "#ebebeb"
-
- Image {
- source: "../images/login-username.svg"
- width: loginDialog.inputHeight * 0.65
- height: width
- sourceSize: Qt.size(width, height);
- anchors {
- verticalCenter: parent.verticalCenter
- left: parent.left
- leftMargin: loginDialog.inputHeight / 4
- }
- }
-
- TextInput {
- id: username
- anchors {
- fill: parent
- leftMargin: loginDialog.inputHeight
- rightMargin: loginDialog.inputHeight / 2
- }
-
- helperText: "username or email"
- color: hifi.colors.text
-
- KeyNavigation.tab: password
- KeyNavigation.backtab: password
- }
- }
-
- Rectangle {
- width: loginDialog.inputWidth
- height: loginDialog.inputHeight
- radius: height / 2
- color: "#ebebeb"
-
- Image {
- source: "../images/login-password.svg"
- width: loginDialog.inputHeight * 0.65
- height: width
- sourceSize: Qt.size(width, height);
- anchors {
- verticalCenter: parent.verticalCenter
- left: parent.left
- leftMargin: loginDialog.inputHeight / 4
- }
- }
-
- TextInput {
- id: password
- anchors {
- fill: parent
- leftMargin: loginDialog.inputHeight
- rightMargin: loginDialog.inputHeight / 2
- }
-
- helperText: "password"
- echoMode: TextInput.Password
- color: hifi.colors.text
-
- KeyNavigation.tab: username
- KeyNavigation.backtab: username
- onFocusChanged: {
- if (password.focus) {
- password.selectAll()
- }
- }
- }
- }
-
- Item {
- width: loginDialog.inputWidth
- height: loginDialog.inputHeight / 2
-
- Text {
- id: messageText
-
- visible: loginDialog.statusText != "" && loginDialog.statusText != "Logging in..."
-
- width: loginDialog.inputWidth
- height: loginDialog.inputHeight / 2
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
-
- text: loginDialog.statusText
- color: "white"
- }
-
- Row {
- id: messageSpinner
-
- visible: loginDialog.statusText == "Logging in..."
- onVisibleChanged: visible ? messageSpinnerAnimation.restart() : messageSpinnerAnimation.stop()
-
- spacing: 24
- anchors {
- verticalCenter: parent.verticalCenter
- horizontalCenter: parent.horizontalCenter
- }
-
- Rectangle {
- id: spinner1
- width: 10
- height: 10
- color: "#ebebeb"
- opacity: 0.05
- }
-
- Rectangle {
- id: spinner2
- width: 10
- height: 10
- color: "#ebebeb"
- opacity: 0.05
- }
-
- Rectangle {
- id: spinner3
- width: 10
- height: 10
- color: "#ebebeb"
- opacity: 0.05
- }
-
- SequentialAnimation {
- id: messageSpinnerAnimation
- running: messageSpinner.visible
- loops: Animation.Infinite
- NumberAnimation { target: spinner1; property: "opacity"; to: 1.0; duration: 1000 }
- NumberAnimation { target: spinner2; property: "opacity"; to: 1.0; duration: 1000 }
- NumberAnimation { target: spinner3; property: "opacity"; to: 1.0; duration: 1000 }
- NumberAnimation { target: spinner1; property: "opacity"; to: 0.05; duration: 0 }
- NumberAnimation { target: spinner2; property: "opacity"; to: 0.05; duration: 0 }
- NumberAnimation { target: spinner3; property: "opacity"; to: 0.05; duration: 0 }
- }
- }
- }
-
- Rectangle {
- width: loginDialog.inputWidth
- height: loginDialog.inputHeight
- radius: height / 2
- color: "#353535"
-
- TextInput {
- anchors.fill: parent
- text: "Login"
- color: "white"
- horizontalAlignment: Text.AlignHCenter
- }
-
- MouseArea {
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- onClicked: {
- loginDialog.login(username.text, password.text)
- }
- }
- }
-
- Item {
- anchors { left: parent.left; right: parent.right; }
- height: loginDialog.inputHeight
-
- Image {
- id: hifiIcon
- source: "../images/hifi-logo-blackish.svg"
- width: loginDialog.inputHeight
- height: width
- sourceSize: Qt.size(width, height);
- anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
- }
-
- Text {
- anchors { verticalCenter: parent.verticalCenter; right: hifiIcon.left; margins: loginDialog.inputSpacing }
- text: "Password?"
- scale: 0.8
- font.underline: true
- color: "#e0e0e0"
- MouseArea {
- anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
- cursorShape: Qt.PointingHandCursor
- onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
- }
- }
-
- Text {
- anchors { verticalCenter: parent.verticalCenter; left: hifiIcon.right; margins: loginDialog.inputSpacing }
- text: "Register"
- scale: 0.8
- font.underline: true
- color: "#e0e0e0"
-
- MouseArea {
- anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
- cursorShape: Qt.PointingHandCursor
- onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/signup")
- }
- }
-
- }
- }
- }
-
- onShownChanged: {
- if (!shown) {
- username.text = ""
- password.text = ""
- loginDialog.statusText = ""
- } else {
- username.forceActiveFocus()
+ Loader {
+ id: bodyLoader
+ anchors.fill: parent
+ source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml"
}
}
Keys.onPressed: {
- switch (event.key) {
+ if (!visible) {
+ return
+ }
+
+ if (event.modifiers === Qt.ControlModifier)
+ switch (event.key) {
+ case Qt.Key_A:
+ event.accepted = true
+ detailedText.selectAll()
+ break
+ case Qt.Key_C:
+ event.accepted = true
+ detailedText.copy()
+ break
+ case Qt.Key_Period:
+ if (Qt.platform.os === "osx") {
+ event.accepted = true
+ content.reject()
+ }
+ break
+ } else switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
- root.shown = false;
- event.accepted = true;
- break;
+ event.accepted = true
+ destroy()
+ break
case Qt.Key_Enter:
case Qt.Key_Return:
- if (username.activeFocus) {
- event.accepted = true
- password.forceActiveFocus()
- } else if (password.activeFocus) {
- event.accepted = true
- if (username.text == "") {
- username.forceActiveFocus()
- } else {
- loginDialog.login(username.text, password.text)
- }
- }
+ event.accepted = true
break
}
}
diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
new file mode 100644
index 0000000000..fc7eac3d00
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
@@ -0,0 +1,125 @@
+//
+// CompleteProfileBody.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0
+import QtQuick 2.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+ id: completeProfileBody
+ clip: true
+ width: pane.width
+ height: pane.height
+
+ QtObject {
+ id: d
+ readonly property int minWidth: 480
+ readonly property int maxWidth: 1280
+ readonly property int minHeight: 120
+ readonly property int maxHeight: 720
+
+ function resize() {
+ var targetWidth = Math.max(titleWidth, additionalTextContainer.contentWidth)
+ var targetHeight = 4 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height
+
+ root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+ root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+ }
+ }
+
+ Row {
+ id: buttons
+ anchors {
+ top: parent.top
+ horizontalCenter: parent.horizontalCenter
+ margins: 0
+ topMargin: 3 * hifi.dimensions.contentSpacing.y
+ }
+ spacing: hifi.dimensions.contentSpacing.x
+ onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 200
+
+ text: qsTr("Create your profile")
+ color: hifi.buttons.blue
+
+ onClicked: loginDialog.createAccountFromStream()
+ }
+
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+
+ text: qsTr("Cancel")
+
+
+ onClicked: root.destroy()
+ }
+ }
+
+ ShortcutText {
+ id: additionalTextContainer
+ anchors {
+ top: buttons.bottom
+ horizontalCenter: parent.horizontalCenter
+ margins: 0
+ topMargin: hifi.dimensions.contentSpacing.y
+ }
+
+ text: "Already have a High Fidelity profile? Link to an existing profile here."
+
+ wrapMode: Text.WordWrap
+ lineHeight: 2
+ lineHeightMode: Text.ProportionalHeight
+ horizontalAlignment: Text.AlignHCenter
+
+ onLinkActivated: {
+ bodyLoader.source = "LinkAccountBody.qml"
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ }
+
+ Component.onCompleted: {
+ root.title = qsTr("Complete Your Profile")
+ root.iconText = "<"
+ d.resize();
+ }
+
+ Connections {
+ target: loginDialog
+ onHandleCreateCompleted: {
+ console.log("Create Succeeded")
+
+ loginDialog.loginThroughSteam()
+ }
+ onHandleCreateFailed: {
+ console.log("Create Failed: " + error)
+
+ bodyLoader.source = "UsernameCollisionBody.qml"
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ onHandleLoginCompleted: {
+ console.log("Login Succeeded")
+
+ bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ onHandleLoginFailed: {
+ console.log("Login Failed")
+ }
+ }
+}
diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
new file mode 100644
index 0000000000..137556964f
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
@@ -0,0 +1,215 @@
+//
+// LinkAccountBody.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0
+import QtQuick 2.4
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+ id: linkAccountBody
+ clip: true
+ width: root.pane.width
+ height: root.pane.height
+
+ function login() {
+ mainTextContainer.visible = false
+ loginDialog.login(usernameField.text, passwordField.text)
+ }
+
+ QtObject {
+ id: d
+ readonly property int minWidth: 480
+ readonly property int maxWidth: 1280
+ readonly property int minHeight: 120
+ readonly property int maxHeight: 720
+
+ function resize() {
+ var targetWidth = Math.max(titleWidth, form.contentWidth)
+ var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
+ 4 * hifi.dimensions.contentSpacing.y + form.height +
+ 4 * hifi.dimensions.contentSpacing.y + buttons.height
+
+ root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+ root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+ }
+ }
+
+ ShortcutText {
+ id: mainTextContainer
+ anchors {
+ top: parent.top
+ left: parent.left
+ margins: 0
+ topMargin: hifi.dimensions.contentSpacing.y
+ }
+
+ visible: false
+
+ text: qsTr("Username or password incorrect.")
+ wrapMode: Text.WordWrap
+ color: hifi.colors.redAccent
+ lineHeight: 1
+ lineHeightMode: Text.ProportionalHeight
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Column {
+ id: form
+ anchors {
+ top: mainTextContainer.bottom
+ left: parent.left
+ margins: 0
+ topMargin: 2 * hifi.dimensions.contentSpacing.y
+ }
+ spacing: 2 * hifi.dimensions.contentSpacing.y
+
+ Row {
+ spacing: hifi.dimensions.contentSpacing.x
+
+ TextField {
+ id: usernameField
+ anchors {
+ verticalCenter: parent.verticalCenter
+ }
+ width: 350
+
+ label: "User Name or Email"
+ }
+
+ ShortcutText {
+ anchors {
+ verticalCenter: parent.verticalCenter
+ }
+
+ text: "Forgot Username?"
+
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+
+ onLinkActivated: loginDialog.openUrl(link)
+ }
+ }
+ Row {
+ spacing: hifi.dimensions.contentSpacing.x
+
+ TextField {
+ id: passwordField
+ anchors {
+ verticalCenter: parent.verticalCenter
+ }
+ width: 350
+
+ label: "Password"
+ echoMode: TextInput.Password
+ }
+
+ ShortcutText {
+ anchors {
+ verticalCenter: parent.verticalCenter
+ }
+
+ text: "Forgot Password?"
+
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+
+ onLinkActivated: loginDialog.openUrl(link)
+ }
+ }
+
+ }
+
+ Row {
+ id: buttons
+ anchors {
+ top: form.bottom
+ right: parent.right
+ margins: 0
+ topMargin: 3 * hifi.dimensions.contentSpacing.y
+ }
+ spacing: hifi.dimensions.contentSpacing.x
+ onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+ Button {
+ id: linkAccountButton
+ anchors.verticalCenter: parent.verticalCenter
+ width: 200
+
+ text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
+ color: hifi.buttons.blue
+
+ onClicked: linkAccountBody.login()
+ }
+
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+
+ text: qsTr("Cancel")
+
+ onClicked: root.destroy()
+ }
+ }
+
+ Component.onCompleted: {
+ root.title = qsTr("Sign Into High Fidelity")
+ root.iconText = "<"
+ d.resize();
+
+ usernameField.forceActiveFocus()
+ }
+
+ Connections {
+ target: loginDialog
+ onHandleLoginCompleted: {
+ console.log("Login Succeeded, linking steam account")
+
+ if (loginDialog.isSteamRunning()) {
+ loginDialog.linkSteam()
+ } else {
+ bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ }
+ onHandleLoginFailed: {
+ console.log("Login Failed")
+ mainTextContainer.visible = true
+ }
+ onHandleLinkCompleted: {
+ console.log("Link Succeeded")
+
+ bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ onHandleLinkFailed: {
+ console.log("Link Failed")
+
+ }
+ }
+
+ Keys.onPressed: {
+ if (!visible) {
+ return
+ }
+
+ switch (event.key) {
+ case Qt.Key_Enter:
+ case Qt.Key_Return:
+ event.accepted = true
+ linkAccountBody.login()
+ break
+ }
+ }
+}
diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml
new file mode 100644
index 0000000000..7bbba683c3
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/SignInBody.qml
@@ -0,0 +1,128 @@
+//
+// SignInBody.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0
+import QtQuick 2.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+ id: signInBody
+ clip: true
+ width: pane.width
+ height: pane.height
+
+ property bool required: false
+
+ function login() {
+ console.log("Trying to log in")
+ loginDialog.loginThroughSteam()
+ }
+
+ function cancel() {
+ root.destroy()
+ }
+
+ QtObject {
+ id: d
+ readonly property int minWidth: 480
+ readonly property int maxWidth: 1280
+ readonly property int minHeight: 120
+ readonly property int maxHeight: 720
+
+ function resize() {
+ var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
+ var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
+
+ root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+ root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+ }
+ }
+
+ InfoItem {
+ id: mainTextContainer
+ anchors {
+ top: parent.top
+ horizontalCenter: parent.horizontalCenter
+ margins: 0
+ topMargin: hifi.dimensions.contentSpacing.y
+ }
+
+ text: required ? qsTr("This domain's owner requires that you sign in:")
+ : qsTr("Sign in to access your user account:")
+ wrapMode: Text.WordWrap
+ color: hifi.colors.baseGrayHighlight
+ lineHeight: 2
+ lineHeightMode: Text.ProportionalHeight
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Row {
+ id: buttons
+ anchors {
+ top: mainTextContainer.bottom
+ horizontalCenter: parent.horizontalCenter
+ margins: 0
+ topMargin: 2 * hifi.dimensions.contentSpacing.y
+ }
+ spacing: hifi.dimensions.contentSpacing.x
+ onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+
+ width: undefined // invalidate so that the image's size sets the width
+ height: undefined // invalidate so that the image's size sets the height
+ focus: true
+
+ style: OriginalStyles.ButtonStyle {
+ background: Image {
+ id: buttonImage
+ source: "../../images/steam-sign-in.png"
+ }
+ }
+ onClicked: signInBody.login()
+ }
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+
+ text: qsTr("Cancel");
+
+ onClicked: signInBody.cancel()
+ }
+ }
+
+ Component.onCompleted: {
+ root.title = required ? qsTr("Sign In Required")
+ : qsTr("Sign In")
+ root.iconText = ""
+ d.resize();
+ }
+
+ Connections {
+ target: loginDialog
+ onHandleLoginCompleted: {
+ console.log("Login Succeeded")
+
+ bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ onHandleLoginFailed: {
+ console.log("Login Failed")
+
+ bodyLoader.source = "CompleteProfileBody.qml"
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ }
+}
diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
new file mode 100644
index 0000000000..b949c660d6
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
@@ -0,0 +1,173 @@
+//
+// UsernameCollisionBody.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0
+import QtQuick 2.4
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+ id: usernameCollisionBody
+ clip: true
+ width: root.pane.width
+ height: root.pane.height
+
+ function create() {
+ mainTextContainer.visible = false
+ loginDialog.createAccountFromStream(textField.text)
+ }
+
+ QtObject {
+ id: d
+ readonly property int minWidth: 480
+ readonly property int maxWidth: 1280
+ readonly property int minHeight: 120
+ readonly property int maxHeight: 720
+
+ function resize() {
+ var targetWidth = Math.max(titleWidth, Math.max(mainTextContainer.contentWidth,
+ termsContainer.contentWidth))
+ var targetHeight = mainTextContainer.height +
+ 2 * hifi.dimensions.contentSpacing.y + textField.height +
+ 5 * hifi.dimensions.contentSpacing.y + termsContainer.height +
+ 1 * hifi.dimensions.contentSpacing.y + buttons.height
+
+ root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+ root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+ }
+ }
+
+ ShortcutText {
+ id: mainTextContainer
+ anchors {
+ top: parent.top
+ left: parent.left
+ margins: 0
+ topMargin: hifi.dimensions.contentSpacing.y
+ }
+
+ text: qsTr("Your Steam username is not available.")
+ wrapMode: Text.WordWrap
+ color: hifi.colors.redAccent
+ lineHeight: 1
+ lineHeightMode: Text.ProportionalHeight
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+
+ TextField {
+ id: textField
+ anchors {
+ top: mainTextContainer.bottom
+ left: parent.left
+ margins: 0
+ topMargin: 2 * hifi.dimensions.contentSpacing.y
+ }
+ width: 250
+
+ placeholderText: "Choose your own"
+ }
+
+ InfoItem {
+ id: termsContainer
+ anchors {
+ top: textField.bottom
+ left: parent.left
+ margins: 0
+ topMargin: 3 * hifi.dimensions.contentSpacing.y
+ }
+
+ text: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service")
+ wrapMode: Text.WordWrap
+ color: hifi.colors.baseGrayHighlight
+ lineHeight: 1
+ lineHeightMode: Text.ProportionalHeight
+ horizontalAlignment: Text.AlignHCenter
+
+ onLinkActivated: loginDialog.openUrl(link)
+ }
+
+ Row {
+ id: buttons
+ anchors {
+ top: termsContainer.bottom
+ right: parent.right
+ margins: 0
+ topMargin: 1 * hifi.dimensions.contentSpacing.y
+ }
+ spacing: hifi.dimensions.contentSpacing.x
+ onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 200
+
+ text: qsTr("Create your profile")
+ color: hifi.buttons.blue
+
+ onClicked: usernameCollisionBody.create()
+ }
+
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+
+ text: qsTr("Cancel")
+
+ onClicked: root.destroy()
+ }
+ }
+
+ Component.onCompleted: {
+ root.title = qsTr("Complete Your Profile")
+ root.iconText = "<"
+ d.resize();
+ }
+ Connections {
+ target: loginDialog
+ onHandleCreateCompleted: {
+ console.log("Create Succeeded")
+
+ loginDialog.loginThroughSteam()
+ }
+ onHandleCreateFailed: {
+ console.log("Create Failed: " + error)
+
+ mainTextContainer.visible = true
+ mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.")
+ }
+ onHandleLoginCompleted: {
+ console.log("Login Succeeded")
+
+ bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
+ bodyLoader.item.width = root.pane.width
+ bodyLoader.item.height = root.pane.height
+ }
+ onHandleLoginFailed: {
+ console.log("Login Failed")
+ }
+ }
+
+ Keys.onPressed: {
+ if (!visible) {
+ return
+ }
+
+ switch (event.key) {
+ case Qt.Key_Enter:
+ case Qt.Key_Return:
+ event.accepted = true
+ usernameCollisionBody.create()
+ break
+ }
+ }
+}
diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml
new file mode 100644
index 0000000000..5fed9addf8
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml
@@ -0,0 +1,90 @@
+//
+// WelcomeBody.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import Hifi 1.0
+import QtQuick 2.4
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+ id: welcomeBody
+ clip: true
+ width: pane.width
+ height: pane.height
+
+ property bool welcomeBack: false
+
+ function setTitle() {
+ root.title = (welcomeBack ? qsTr("Welcome back ") : qsTr("Welcome ")) + Account.username + qsTr("!")
+ root.iconText = ""
+ d.resize();
+ }
+
+ QtObject {
+ id: d
+ readonly property int minWidth: 480
+ readonly property int maxWidth: 1280
+ readonly property int minHeight: 120
+ readonly property int maxHeight: 720
+
+ function resize() {
+ var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
+ var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
+
+ root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+ root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+ }
+ }
+
+ InfoItem {
+ id: mainTextContainer
+ anchors {
+ top: parent.top
+ horizontalCenter: parent.horizontalCenter
+ margins: 0
+ topMargin: hifi.dimensions.contentSpacing.y
+ }
+
+ text: qsTr("You are now signed into High Fidelity")
+ wrapMode: Text.WordWrap
+ color: hifi.colors.baseGrayHighlight
+ lineHeight: 2
+ lineHeightMode: Text.ProportionalHeight
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Row {
+ id: buttons
+ anchors {
+ top: mainTextContainer.bottom
+ horizontalCenter: parent.horizontalCenter
+ margins: 0
+ topMargin: 2 * hifi.dimensions.contentSpacing.y
+ }
+ spacing: hifi.dimensions.contentSpacing.x
+ onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+ Button {
+ anchors.verticalCenter: parent.verticalCenter
+
+ text: qsTr("Close");
+
+ onClicked: root.destroy()
+ }
+ }
+
+ Component.onCompleted: welcomeBody.setTitle()
+
+ Connections {
+ target: Account
+ onUsernameChanged: welcomeBody.setTitle()
+ }
+}
diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml
index fe88899658..180e5e1bcc 100644
--- a/interface/resources/qml/Stats.qml
+++ b/interface/resources/qml/Stats.qml
@@ -99,6 +99,12 @@ Item {
font.pixelSize: root.fontSize
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
}
+ Text {
+ color: root.fontColor;
+ font.pixelSize: root.fontSize
+ visible: root.expanded
+ text: "Asset Mbps In/Out: " + root.assetMbpsIn.toFixed(2) + "/" + root.assetMbpsOut.toFixed(2)
+ }
}
}
diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml
new file mode 100644
index 0000000000..425500f1d4
--- /dev/null
+++ b/interface/resources/qml/controls-uit/HorizontalRule.qml
@@ -0,0 +1,20 @@
+//
+// HorizontalRule.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+
+Rectangle {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 1
+ color: hifi.colors.lightGray
+}
diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml
new file mode 100644
index 0000000000..545154ab44
--- /dev/null
+++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml
@@ -0,0 +1,21 @@
+//
+// HorizontalSpacer.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+
+import "../styles-uit"
+
+Item {
+ id: root
+ property alias size: root.width
+
+ width: hifi.dimensions.controlInterlineHeight
+ height: 1 // Must be non-zero
+}
diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml
index 6fc49605c0..2df65f1002 100644
--- a/interface/resources/qml/controls-uit/VerticalSpacer.qml
+++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml
@@ -13,6 +13,9 @@ import QtQuick 2.5
import "../styles-uit"
Item {
+ id: root
+ property alias size: root.height
+
width: 1 // Must be non-zero
height: hifi.dimensions.controlInterlineHeight
}
diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml
index d390ea08bf..40c5a01e15 100644
--- a/interface/resources/qml/dialogs/MessageDialog.qml
+++ b/interface/resources/qml/dialogs/MessageDialog.qml
@@ -21,8 +21,6 @@ import "messageDialog"
ModalWindow {
id: root
HifiConstants { id: hifi }
- implicitWidth: 640
- implicitHeight: 320
destroyOnCloseButton: true
destroyOnHidden: true
visible: true
@@ -70,7 +68,7 @@ ModalWindow {
QtObject {
id: d
readonly property int minWidth: 480
- readonly property int maxWdith: 1280
+ readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
@@ -80,7 +78,7 @@ ModalWindow {
+ (informativeTextContainer.text != "" ? informativeTextContainer.contentHeight + 3 * hifi.dimensions.contentSpacing.y : 0)
+ buttons.height
+ (content.state === "expanded" ? details.implicitHeight + hifi.dimensions.contentSpacing.y : 0)
- root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
+ root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWidth) ? d.maxWidth : targetWidth)
root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)
}
}
diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
index 04e3934535..6d371741ea 100644
--- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
+++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
@@ -24,6 +24,7 @@ Item {
Rectangle { color: hifi.colors.baseGray; anchors.fill: parent; radius: 4 }
Component.onCompleted: {
+ jointChooser.model = MyAvatar.jointNames;
completed = true;
}
@@ -82,7 +83,6 @@ Item {
HifiControls.ComboBox {
id: jointChooser;
anchors { bottom: parent.bottom; left: parent.left; right: parent.right }
- model: MyAvatar.jointNames
colorScheme: hifi.colorSchemes.dark
currentIndex: attachment ? model.indexOf(attachment.jointName) : -1
onCurrentIndexChanged: {
diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml
new file mode 100644
index 0000000000..aade5fb439
--- /dev/null
+++ b/interface/resources/qml/styles-uit/ButtonLabel.qml
@@ -0,0 +1,18 @@
+//
+// ButtonLabel.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayBold {
+ font.pixelSize: hifi.fontSizes.buttonLabel
+}
diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml
new file mode 100644
index 0000000000..84c1ef14c1
--- /dev/null
+++ b/interface/resources/qml/styles-uit/IconButton.qml
@@ -0,0 +1,20 @@
+//
+// IconButton.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+ font.pixelSize: hifi.fontSizes.iconButton
+ font.capitalization: Font.AllUppercase
+ font.letterSpacing: 1.5
+}
diff --git a/interface/resources/qml/styles-uit/InfoItem.qml b/interface/resources/qml/styles-uit/InfoItem.qml
new file mode 100644
index 0000000000..83781a4ef5
--- /dev/null
+++ b/interface/resources/qml/styles-uit/InfoItem.qml
@@ -0,0 +1,19 @@
+//
+// InfoItem.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewaySemiBold {
+ lineHeight: 2
+ font.pixelSize: hifi.fontSizes.menuItem
+}
diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml
new file mode 100644
index 0000000000..59657a554d
--- /dev/null
+++ b/interface/resources/qml/styles-uit/InputLabel.qml
@@ -0,0 +1,18 @@
+//
+// InputLabel.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewaySemiBold {
+ font.pixelSize: hifi.fontSizes.inputLabel
+}
diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml
new file mode 100644
index 0000000000..f707686edc
--- /dev/null
+++ b/interface/resources/qml/styles-uit/ListItem.qml
@@ -0,0 +1,18 @@
+//
+// ListItem.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+ font.pixelSize: hifi.fontSizes.listItem
+}
diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml
new file mode 100644
index 0000000000..577fe2f8d8
--- /dev/null
+++ b/interface/resources/qml/styles-uit/Logs.qml
@@ -0,0 +1,18 @@
+//
+// Logs.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+AnonymousProRegular {
+ font.pixelSize: hifi.fontSizes.logs
+}
diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml
new file mode 100644
index 0000000000..e23b9eca14
--- /dev/null
+++ b/interface/resources/qml/styles-uit/OverlayTitle.qml
@@ -0,0 +1,18 @@
+//
+// OverlayTitle.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+ font.pixelSize: hifi.fontSizes.overlayTitle
+}
diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml
new file mode 100644
index 0000000000..5438fec7bc
--- /dev/null
+++ b/interface/resources/qml/styles-uit/SectionName.qml
@@ -0,0 +1,19 @@
+//
+// SectionName.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+ font.pixelSize: hifi.fontSizes.sectionName
+ font.capitalization: Font.AllUppercase
+}
diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml
new file mode 100644
index 0000000000..a3ab351870
--- /dev/null
+++ b/interface/resources/qml/styles-uit/ShortcutText.qml
@@ -0,0 +1,18 @@
+//
+// ShortcutText.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayLight {
+ font.pixelSize: hifi.fontSizes.shortcutText
+}
diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml
new file mode 100644
index 0000000000..eb4e790e7e
--- /dev/null
+++ b/interface/resources/qml/styles-uit/TabName.qml
@@ -0,0 +1,19 @@
+//
+// TabName.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+ font.pixelSize: hifi.fontSizes.tabName
+ font.capitalization: Font.AllUppercase
+}
diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml
new file mode 100644
index 0000000000..010b4d03ad
--- /dev/null
+++ b/interface/resources/qml/styles-uit/TextFieldInput.qml
@@ -0,0 +1,18 @@
+//
+// TextFieldInput.qml
+//
+// Created by Clement on 7/18/16
+// Copyright 2016 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+FiraSansSemiBold {
+ font.pixelSize: hifi.fontSizes.textFieldInput
+}
diff --git a/interface/resources/shaders/hmd_hand_lasers.frag b/interface/resources/shaders/hmd_hand_lasers.frag
deleted file mode 100644
index 6fffb1c521..0000000000
--- a/interface/resources/shaders/hmd_hand_lasers.frag
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// Created by Bradley Austin Davis on 2016/07/11
-// Copyright 2013-2016 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#version 410 core
-
-uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
-
-layout(location = 0) in vec3 inLineDistance;
-
-out vec4 FragColor;
-
-void main() {
- vec2 d = inLineDistance.xy;
- d.y = abs(d.y);
- d.x = abs(d.x);
- if (d.x > 1.0) {
- d.x = (d.x - 1.0) / 0.02;
- } else {
- d.x = 0.0;
- }
- float alpha = 1.0 - length(d);
- if (alpha <= 0.0) {
- discard;
- }
- alpha = pow(alpha, 10.0);
- if (alpha < 0.05) {
- discard;
- }
- FragColor = vec4(color.rgb, alpha);
-}
diff --git a/interface/resources/shaders/hmd_hand_lasers.geom b/interface/resources/shaders/hmd_hand_lasers.geom
deleted file mode 100644
index 16b5dafadd..0000000000
--- a/interface/resources/shaders/hmd_hand_lasers.geom
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-// Created by Bradley Austin Davis on 2016/07/11
-// Copyright 2013-2016 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-#version 410 core
-#extension GL_EXT_geometry_shader4 : enable
-
-layout(location = 0) out vec3 outLineDistance;
-
-layout(lines) in;
-layout(triangle_strip, max_vertices = 24) out;
-
-vec3[2] getOrthogonals(in vec3 n, float scale) {
- float yDot = abs(dot(n, vec3(0, 1, 0)));
-
- vec3 result[2];
- if (yDot < 0.9) {
- result[0] = normalize(cross(n, vec3(0, 1, 0)));
- } else {
- result[0] = normalize(cross(n, vec3(1, 0, 0)));
- }
- // The cross of result[0] and n is orthogonal to both, which are orthogonal to each other
- result[1] = cross(result[0], n);
- result[0] *= scale;
- result[1] *= scale;
- return result;
-}
-
-
-vec2 orthogonal(vec2 v) {
- vec2 result = v.yx;
- result.y *= -1.0;
- return result;
-}
-
-void main() {
- vec2 endpoints[2];
- for (int i = 0; i < 2; ++i) {
- endpoints[i] = gl_PositionIn[i].xy / gl_PositionIn[i].w;
- }
- vec2 lineNormal = normalize(endpoints[1] - endpoints[0]);
- vec2 lineOrthogonal = orthogonal(lineNormal);
- lineNormal *= 0.02;
- lineOrthogonal *= 0.02;
-
- gl_Position = gl_PositionIn[0];
- gl_Position.xy -= lineOrthogonal;
- outLineDistance = vec3(-1.02, -1, gl_Position.z);
- EmitVertex();
-
- gl_Position = gl_PositionIn[0];
- gl_Position.xy += lineOrthogonal;
- outLineDistance = vec3(-1.02, 1, gl_Position.z);
- EmitVertex();
-
- gl_Position = gl_PositionIn[1];
- gl_Position.xy -= lineOrthogonal;
- outLineDistance = vec3(1.02, -1, gl_Position.z);
- EmitVertex();
-
- gl_Position = gl_PositionIn[1];
- gl_Position.xy += lineOrthogonal;
- outLineDistance = vec3(1.02, 1, gl_Position.z);
- EmitVertex();
-
- EndPrimitive();
-}
diff --git a/interface/resources/shaders/hmd_hand_lasers.vert b/interface/resources/shaders/hmd_hand_lasers.vert
deleted file mode 100644
index db5c7c1ecd..0000000000
--- a/interface/resources/shaders/hmd_hand_lasers.vert
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// Created by Bradley Austin Davis on 2016/07/11
-// Copyright 2013-2016 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-#version 410 core
-uniform mat4 mvp = mat4(1);
-
-in vec3 Position;
-
-void main() {
- gl_Position = mvp * vec4(Position, 1);
-}
diff --git a/interface/resources/shaders/hmd_ui_glow.frag b/interface/resources/shaders/hmd_ui_glow.frag
index 733f32d718..9270842092 100644
--- a/interface/resources/shaders/hmd_ui_glow.frag
+++ b/interface/resources/shaders/hmd_ui_glow.frag
@@ -6,18 +6,27 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-#version 410 core
-
uniform sampler2D sampler;
-uniform float alpha = 1.0;
-uniform vec4 glowPoints = vec4(-1);
-uniform vec4 glowColors[2];
-uniform vec2 resolution = vec2(3960.0, 1188.0);
-uniform float radius = 0.005;
+
+struct OverlayData {
+ mat4 mvp;
+ vec4 glowPoints;
+ vec4 glowColors[2];
+ vec4 resolutionRadiusAlpha;
+};
+
+layout(std140) uniform overlayBuffer {
+ OverlayData overlay;
+};
+
+vec2 resolution = overlay.resolutionRadiusAlpha.xy;
+float radius = overlay.resolutionRadiusAlpha.z;
+float alpha = overlay.resolutionRadiusAlpha.w;
+vec4 glowPoints = overlay.glowPoints;
+vec4 glowColors[2] = overlay.glowColors;
in vec3 vPosition;
in vec2 vTexCoord;
-in vec4 vGlowPoints;
out vec4 FragColor;
@@ -31,9 +40,10 @@ float easeInOutCubic(float f) {
}
void main() {
+ FragColor = texture(sampler, vTexCoord);
+
vec2 aspect = resolution;
aspect /= resolution.x;
- FragColor = texture(sampler, vTexCoord);
float glowIntensity = 0.0;
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);
diff --git a/interface/resources/shaders/hmd_ui_glow.vert b/interface/resources/shaders/hmd_ui_glow.vert
index 5defec085f..54eb062590 100644
--- a/interface/resources/shaders/hmd_ui_glow.vert
+++ b/interface/resources/shaders/hmd_ui_glow.vert
@@ -6,12 +6,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-#version 410 core
+struct OverlayData {
+ mat4 mvp;
+ vec4 glowPoints;
+ vec4 glowColors[2];
+ vec4 resolutionRadiusAlpha;
+};
-uniform mat4 mvp = mat4(1);
+layout(std140) uniform overlayBuffer {
+ OverlayData overlay;
+};
-in vec3 Position;
-in vec2 TexCoord;
+mat4 mvp = overlay.mvp;
+
+layout(location = 0) in vec3 Position;
+layout(location = 3) in vec2 TexCoord;
out vec3 vPosition;
out vec2 vTexCoord;
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 66fb6ed6b6..f32ceadc92 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -87,15 +87,16 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
-#include
+#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -140,7 +141,6 @@
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
-#include "Stars.h"
#include "ui/AddressBarDialog.h"
#include "ui/AvatarInputs.h"
#include "ui/DialogsManager.h"
@@ -258,7 +258,10 @@ public:
void run() override {
while (!_quit) {
QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
-
+ // Don't do heartbeat detection under nsight
+ if (nsightActive()) {
+ continue;
+ }
uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us
uint64_t now = usecTimestampNow();
auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0;
@@ -306,8 +309,6 @@ public:
// Don't actually crash in debug builds, in case this apparent deadlock is simply from
// the developer actively debugging code
#ifdef NDEBUG
-
-
deadlockDetectionCrash();
#endif
}
@@ -392,6 +393,11 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
}
static const QString STATE_IN_HMD = "InHMD";
+static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM";
+static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson";
+static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson";
+static const QString STATE_CAMERA_ENTITY = "CameraEntity";
+static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent";
static const QString STATE_SNAP_TURN = "SnapTurn";
static const QString STATE_GROUNDED = "Grounded";
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
@@ -471,7 +477,9 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set();
DependencyManager::set();
DependencyManager::set();
- controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } });
+ controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
+ STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT,
+ STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } });
DependencyManager::set();
DependencyManager::set();
DependencyManager::set();
@@ -740,7 +748,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);
identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
- ResourceCache::setRequestLimit(MAX_CONCURRENT_RESOURCE_DOWNLOADS);
+ const char** constArgv = const_cast(argv);
+ QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads");
+ bool success;
+ int concurrentDownloads = concurrentDownloadsStr.toInt(&success);
+ if (!success) {
+ concurrentDownloads = MAX_CONCURRENT_RESOURCE_DOWNLOADS;
+ }
+ ResourceCache::setRequestLimit(concurrentDownloads);
_glWidget = new GLCanvas();
getApplicationCompositor().setRenderingWidget(_glWidget);
@@ -766,16 +781,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_glWidget->makeCurrent();
_glWidget->initializeGL();
- _chromiumShareContext = new OffscreenGLCanvas();
- _chromiumShareContext->create(_glWidget->context()->contextHandle());
- _chromiumShareContext->makeCurrent();
- qt_gl_set_global_share_context(_chromiumShareContext->getContext());
-
- _offscreenContext = new OffscreenGLCanvas();
- _offscreenContext->create(_glWidget->context()->contextHandle());
- _offscreenContext->makeCurrent();
initializeGL();
- _offscreenContext->makeCurrent();
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@@ -812,6 +818,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
}
UserActivityLogger::getInstance().logAction("launch", properties);
+ _connectionMonitor.init();
// Tell our entity edit sender about our known jurisdictions
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
@@ -832,7 +839,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
QSharedPointer bandwidthRecorder = DependencyManager::get();
connect(nodeList.data(), &LimitedNodeList::dataSent,
bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);
- connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,
+ connect(nodeList.data(), &LimitedNodeList::dataReceived,
bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
// FIXME -- I'm a little concerned about this.
@@ -955,6 +962,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_applicationStateDevice->setInputVariant(STATE_IN_HMD, []() -> float {
return qApp->isHMDMode() ? 1 : 0;
});
+ _applicationStateDevice->setInputVariant(STATE_CAMERA_FULL_SCREEN_MIRROR, []() -> float {
+ return qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR ? 1 : 0;
+ });
+ _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float {
+ return qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0;
+ });
+ _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float {
+ return qApp->getCamera()->getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
+ });
+ _applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float {
+ return qApp->getCamera()->getMode() == CAMERA_MODE_ENTITY ? 1 : 0;
+ });
+ _applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float {
+ return qApp->getCamera()->getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
+ });
_applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
});
@@ -1205,6 +1227,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
+ auto textureCache = DependencyManager::get();
+
+ QString skyboxUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.jpg" };
+ QString skyboxAmbientUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-ambient.jpg" };
+
+ _defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", false } });
+ _defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } });
+
+ _defaultSkybox->setCubemap(_defaultSkyboxTexture);
+ _defaultSkybox->setColor({ 1.0, 1.0, 1.0 });
+
+ EntityItem::setEntitiesShouldFadeFunction([this]() {
+ SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer);
+ return entityServerNode && !isPhysicsEnabled();
+ });
+
// After all of the constructor is completed, then set firstRun to false.
Setting::Handle firstRun{ Settings::firstRun, true };
firstRun.set(false);
@@ -1458,11 +1496,18 @@ void Application::initializeGL() {
_isGLInitialized = true;
}
+ _glWidget->makeCurrent();
+ _chromiumShareContext = new OffscreenGLCanvas();
+ _chromiumShareContext->create(_glWidget->context()->contextHandle());
+ _chromiumShareContext->makeCurrent();
+ qt_gl_set_global_share_context(_chromiumShareContext->getContext());
+
+ _glWidget->makeCurrent();
gpu::Context::init();
_gpuContext = std::make_shared();
// The gpu context can make child contexts for transfers, so
// we need to restore primary rendering context
- _offscreenContext->makeCurrent();
+ _glWidget->makeCurrent();
initDisplay();
qCDebug(interfaceapp, "Initialized Display.");
@@ -1481,7 +1526,8 @@ void Application::initializeGL() {
// Needs to happen AFTER the render engine initialization to access its configuration
initializeUi();
qCDebug(interfaceapp, "Initialized Offscreen UI.");
- _offscreenContext->makeCurrent();
+ _glWidget->makeCurrent();
+
// call Menu getInstance static method to set up the menu
// Needs to happen AFTER the QML UI initialization
@@ -1497,8 +1543,13 @@ void Application::initializeGL() {
_idleLoopStdev.reset();
+ _offscreenContext = new OffscreenGLCanvas();
+ _offscreenContext->create(_glWidget->context()->contextHandle());
+ _offscreenContext->makeCurrent();
+
// update before the first render
update(0);
+
}
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
@@ -1515,7 +1566,7 @@ void Application::initializeUi() {
auto offscreenUi = DependencyManager::get();
- offscreenUi->create(_offscreenContext->getContext());
+ offscreenUi->create(_glWidget->context()->contextHandle());
auto rootContext = offscreenUi->getRootContext();
@@ -1544,7 +1595,7 @@ void Application::initializeUi() {
rootContext->setContextProperty("Entities", DependencyManager::get().data());
FileScriptingInterface* fileDownload = new FileScriptingInterface(engine);
rootContext->setContextProperty("File", fileDownload);
- connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::toggleAssetServerWidget);
+ connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::showAssetServerWidget);
rootContext->setContextProperty("MyAvatar", getMyAvatar());
rootContext->setContextProperty("Messages", DependencyManager::get().data());
rootContext->setContextProperty("Recording", DependencyManager::get().data());
@@ -1595,6 +1646,8 @@ void Application::initializeUi() {
rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
+
+ rootContext->setContextProperty("Steam", new SteamScriptingInterface(engine));
_glWidget->installEventFilter(offscreenUi.data());
@@ -1687,17 +1740,7 @@ void Application::paintGL() {
PerformanceWarning warn(showWarnings, "Application::paintGL()");
resizeGL();
- // Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened
- {
- PerformanceTimer perfTimer("syncCache");
- renderArgs._context->syncCache();
- }
-
- auto framebufferCache = DependencyManager::get();
- // Final framebuffer that will be handled to the display-plugin
- auto finalFramebuffer = framebufferCache->getFramebuffer();
-
- _gpuContext->beginFrame(finalFramebuffer, getHMDSensorPose());
+ _gpuContext->beginFrame(getHMDSensorPose());
// Reset the gpu::Context Stages
// Back to the default framebuffer;
gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) {
@@ -1827,7 +1870,10 @@ void Application::paintGL() {
getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform());
// Primary rendering pass
+ auto framebufferCache = DependencyManager::get();
const QSize size = framebufferCache->getFrameBufferSize();
+ // Final framebuffer that will be handled to the display-plugin
+ auto finalFramebuffer = framebufferCache->getFramebuffer();
{
PROFILE_RANGE(__FUNCTION__ "/mainRender");
@@ -1852,7 +1898,6 @@ void Application::paintGL() {
auto baseProjection = renderArgs.getViewFrustum().getProjection();
auto hmdInterface = DependencyManager::get();
float IPDScale = hmdInterface->getIPDScale();
- mat4 headPose = displayPlugin->getHeadPose();
// FIXME we probably don't need to set the projection matrix every frame,
// only when the display plugin changes (or in non-HMD modes when the user
@@ -1868,13 +1913,6 @@ void Application::paintGL() {
// Apply IPD scaling
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
eyeOffsets[eye] = eyeOffsetTransform;
-
- // Tell the plugin what pose we're using to render. In this case we're just using the
- // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
- // for rotational timewarp. If we move to support positonal timewarp, we need to
- // ensure this contains the full pose composed with the eye offsets.
- displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform));
-
eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
});
renderArgs._context->setStereoProjections(eyeProjections);
@@ -1882,36 +1920,26 @@ void Application::paintGL() {
}
renderArgs._blitFramebuffer = finalFramebuffer;
displaySide(&renderArgs, _myCamera);
-
- renderArgs._blitFramebuffer.reset();
- renderArgs._context->enableStereo(false);
}
- _gpuContext->endFrame();
-
- gpu::TexturePointer overlayTexture = _applicationOverlay.acquireOverlay();
- if (overlayTexture) {
- displayPlugin->submitOverlayTexture(overlayTexture);
- }
-
- // deliver final composited scene to the display plugin
+ auto frame = _gpuContext->endFrame();
+ frame->frameIndex = _frameCount;
+ frame->framebuffer = finalFramebuffer;
+ frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
+ DependencyManager::get()->releaseFramebuffer(framebuffer);
+ };
+ frame->overlay = _applicationOverlay.getOverlayTexture();
+ // deliver final scene rendering commands to the display plugin
{
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
PerformanceTimer perfTimer("pluginOutput");
-
- auto finalTexture = finalFramebuffer->getRenderBuffer(0);
- Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
- _lockedFramebufferMap[finalTexture] = finalFramebuffer;
-
- Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
- {
- PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
- PerformanceTimer perfTimer("pluginSubmitScene");
- displayPlugin->submitSceneTexture(_frameCount, finalTexture);
- }
- Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
+ displayPlugin->submitFrame(frame);
}
+ // Reset the framebuffer and stereo state
+ renderArgs._blitFramebuffer.reset();
+ renderArgs._context->enableStereo(false);
+
{
Stats::getInstance()->setRenderDetails(renderArgs._details);
}
@@ -2277,7 +2305,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
case Qt::Key_Asterisk:
- Menu::getInstance()->triggerOption(MenuOption::Stars);
+ Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox);
break;
case Qt::Key_S:
@@ -2908,6 +2936,8 @@ void Application::idle(float nsecsElapsed) {
PROFILE_RANGE(__FUNCTION__);
+ SteamClient::runCallbacks();
+
float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND;
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
@@ -3216,6 +3246,14 @@ void Application::init() {
addressLookupString = arguments().value(urlIndex + 1);
}
+ // when +connect_lobby in command line, join steam lobby
+ const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby";
+ int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY);
+ if (lobbyIndex != -1) {
+ QString lobbyId = arguments().value(lobbyIndex + 1);
+ SteamClient::joinLobby(lobbyId);
+ }
+
Setting::Handle firstRun { Settings::firstRun, true };
if (addressLookupString.isEmpty() && firstRun.get()) {
qDebug() << "First run and no URL passed... attempting to go to Home or Entry...";
@@ -3245,6 +3283,18 @@ void Application::init() {
getEntities()->setViewFrustum(_viewFrustum);
}
+ getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) {
+ auto dims = item.getDimensions();
+ auto maxSize = glm::max(dims.x, dims.y, dims.z);
+
+ if (maxSize <= 0.0f) {
+ return 0.0f;
+ }
+
+ auto distance = glm::distance(getMyAvatar()->getPosition(), item.getPosition());
+ return atan2(maxSize, distance);
+ });
+
ObjectMotionState::setShapeManager(&_shapeManager);
_physicsEngine->init();
@@ -4190,8 +4240,6 @@ public:
typedef render::Payload Payload;
typedef Payload::DataPointer Pointer;
- Stars _stars;
-
static render::ItemID _item; // unique WorldBoxRenderData
};
@@ -4215,6 +4263,21 @@ namespace render {
auto backgroundMode = skyStage->getBackgroundMode();
switch (backgroundMode) {
+ case model::SunSkyStage::SKY_DEFAULT: {
+ static const glm::vec3 DEFAULT_SKYBOX_COLOR{ 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f };
+ static const float DEFAULT_SKYBOX_INTENSITY{ 0.2f };
+ static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY{ 2.0f };
+ static const glm::vec3 DEFAULT_SKYBOX_DIRECTION{ 0.0f, 0.0f, -1.0f };
+
+ auto scene = DependencyManager::get()->getStage();
+ auto sceneKeyLight = scene->getKeyLight();
+ scene->setSunModelEnable(false);
+ sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR);
+ sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY);
+ sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY);
+ sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION);
+ // fall through: render a skybox, if available
+ }
case model::SunSkyStage::SKY_BOX: {
auto skybox = skyStage->getSkybox();
if (skybox) {
@@ -4222,22 +4285,22 @@ namespace render {
skybox->render(batch, args->getViewFrustum());
break;
}
+ // fall through: render defaults, if available
}
-
- // Fall through: if no skybox is available, render the SKY_DOME
- case model::SunSkyStage::SKY_DOME: {
- if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
- PerformanceTimer perfTimer("stars");
- PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
- "Application::payloadRender() ... My god, it's full of stars...");
- // should be the first rendering pass - w/o depth buffer / lighting
-
- static const float alpha = 1.0f;
- background->_stars.render(args, alpha);
+ case model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: {
+ if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
+ auto scene = DependencyManager::get()->getStage();
+ auto sceneKeyLight = scene->getKeyLight();
+ auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
+ // do not set the ambient sphere - it peaks too high, and causes flashing when turning
+ sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
}
+ // fall through: render defaults, if available
}
- break;
-
+ case model::SunSkyStage::SKY_DEFAULT_TEXTURE:
+ if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
+ qApp->getDefaultSkybox()->render(batch, args->getViewFrustum());
+ }
case model::SunSkyStage::NO_BACKGROUND:
default:
// this line intentionally left blank
@@ -4428,7 +4491,6 @@ void Application::updateWindowTitle() const {
#endif
_window->setWindowTitle(title);
}
-
void Application::clearDomainOctreeDetails() {
// if we're about to quit, we really don't need to do any of these things...
@@ -4454,7 +4516,8 @@ void Application::clearDomainOctreeDetails() {
getEntities()->clear();
auto skyStage = DependencyManager::get()->getSkyStage();
- skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME);
+
+ skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT);
_recentlyClearedDomain = true;
}
@@ -4781,6 +4844,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().data());
scriptEngine->registerGlobalObject("Users", DependencyManager::get().data());
+
+ scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine));
}
bool Application::canAcceptURL(const QString& urlString) const {
@@ -4819,7 +4884,7 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
}
if (defaultUpload) {
- toggleAssetServerWidget(urlString);
+ showAssetServerWidget(urlString);
}
return defaultUpload;
}
@@ -5006,17 +5071,15 @@ void Application::toggleRunningScriptsWidget() const {
//}
}
-void Application::toggleAssetServerWidget(QString filePath) {
- qDebug() << "toggle asset before if: " + filePath;
+
+void Application::showAssetServerWidget(QString filePath) {
if (!DependencyManager::get()->getThisNodeCanWriteAssets()) {
return;
}
- qDebug() << "toggle asset after if";
static const QUrl url { "AssetServer.qml" };
auto startUpload = [=](QQmlContext* context, QObject* newObject){
if (!filePath.isEmpty()) {
- qDebug() << "file in toggle: " + filePath;
emit uploadRequest(filePath);
}
};
@@ -5059,8 +5122,10 @@ void Application::setPreviousScriptLocation(const QString& location) {
}
void Application::loadScriptURLDialog() const {
- auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL");
- if (!newScript.isEmpty()) {
+ auto newScript = OffscreenUi::getText(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL");
+ if (QUrl(newScript).scheme() == "atp") {
+ OffscreenUi::warning("Error Loading Script", "Cannot load client script over ATP");
+ } else if (!newScript.isEmpty()) {
DependencyManager::get()->loadScript(newScript);
}
}
@@ -5333,6 +5398,7 @@ void Application::updateDisplayMode() {
DisplayPluginList advanced;
DisplayPluginList developer;
foreach(auto displayPlugin, displayPlugins) {
+ displayPlugin->setContext(_gpuContext);
auto grouping = displayPlugin->getGrouping();
switch (grouping) {
case Plugin::ADVANCED:
@@ -5402,9 +5468,6 @@ void Application::updateDisplayMode() {
_displayPlugin->deactivate();
}
- // FIXME probably excessive and useless context switching
- _offscreenContext->makeCurrent();
-
bool active = newDisplayPlugin->activate();
if (!active) {
@@ -5549,20 +5612,6 @@ bool Application::makeRenderingContextCurrent() {
return _offscreenContext->makeCurrent();
}
-void Application::releaseSceneTexture(const gpu::TexturePointer& texture) {
- Q_ASSERT(QThread::currentThread() == thread());
- auto& framebufferMap = _lockedFramebufferMap;
- Q_ASSERT(framebufferMap.contains(texture));
- auto framebufferPointer = framebufferMap[texture];
- framebufferMap.remove(texture);
- auto framebufferCache = DependencyManager::get();
- framebufferCache->releaseFramebuffer(framebufferPointer);
-}
-
-void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) {
- _applicationOverlay.releaseOverlay(texture);
-}
-
bool Application::isForeground() const {
return _isForeground && !_window->isMinimized();
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 0af65f665f..890dd51b15 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -47,6 +47,7 @@
#include "avatar/MyAvatar.h"
#include "Bookmarks.h"
#include "Camera.h"
+#include "ConnectionMonitor.h"
#include "FileLogger.h"
#include "gpu/Context.h"
#include "Menu.h"
@@ -64,6 +65,9 @@
#include "ui/overlays/Overlays.h"
#include "UndoStackScriptingInterface.h"
+#include
+#include
+
class OffscreenGLCanvas;
class GLCanvas;
class FaceTracker;
@@ -108,8 +112,6 @@ public:
virtual MainWindow* getPrimaryWindow() override;
virtual QOpenGLContext* getPrimaryContext() override;
virtual bool makeRenderingContextCurrent() override;
- virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
- virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
virtual bool isForeground() const override;
virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
@@ -248,6 +250,10 @@ public:
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
+ model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
+ gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; }
+ gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; }
+
signals:
void svoImportRequested(const QString& url);
@@ -269,7 +275,7 @@ public slots:
Q_INVOKABLE void loadScriptURLDialog() const;
void toggleLogDialog();
void toggleRunningScriptsWidget() const;
- void toggleAssetServerWidget(QString filePath = "");
+ Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
void handleLocalServerConnection() const;
void readArgumentsFromLocalSocket() const;
@@ -426,7 +432,6 @@ private:
InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false };
- QMap _lockedFramebufferMap;
QUndoStack _undoStack;
UndoStackScriptingInterface _undoStackScriptingInterface;
@@ -562,6 +567,13 @@ private:
bool _recentlyClearedDomain { false };
QString _returnFromFullScreenMirrorTo;
+
+ ConnectionMonitor _connectionMonitor;
+
+ model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
+ gpu::TexturePointer _defaultSkyboxTexture;
+ gpu::TexturePointer _defaultSkyboxAmbientTexture;
};
+
#endif // hifi_Application_h
diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp
new file mode 100644
index 0000000000..1956ace67b
--- /dev/null
+++ b/interface/src/ConnectionMonitor.cpp
@@ -0,0 +1,52 @@
+//
+// ConnectionMonitor.cpp
+// interface/src
+//
+// Created by Ryan Huffman on 8/4/15.
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "ConnectionMonitor.h"
+
+#include "ui/DialogsManager.h"
+
+#include
+#include
+#include
+#include
+
+static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000;
+
+void ConnectionMonitor::init() {
+ // Connect to domain disconnected message
+ auto nodeList = DependencyManager::get();
+ const DomainHandler& domainHandler = nodeList->getDomainHandler();
+ connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &ConnectionMonitor::disconnectedFromDomain);
+ connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::connectedToDomain);
+
+ // Connect to AddressManager::hostChanged
+ auto addressManager = DependencyManager::get();
+ connect(addressManager.data(), &AddressManager::hostChanged, this, &ConnectionMonitor::hostChanged);
+
+ _timer.setSingleShot(true);
+ _timer.setInterval(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS);
+ _timer.start();
+
+ auto dialogsManager = DependencyManager::get();
+ connect(&_timer, &QTimer::timeout, dialogsManager.data(), &DialogsManager::showAddressBar);
+}
+
+void ConnectionMonitor::disconnectedFromDomain() {
+ _timer.start();
+}
+
+void ConnectionMonitor::connectedToDomain(const QString& name) {
+ _timer.stop();
+}
+
+void ConnectionMonitor::hostChanged(const QString& name) {
+ _timer.start();
+}
diff --git a/interface/src/ConnectionMonitor.h b/interface/src/ConnectionMonitor.h
new file mode 100644
index 0000000000..bba420715e
--- /dev/null
+++ b/interface/src/ConnectionMonitor.h
@@ -0,0 +1,34 @@
+//
+// ConnectionMonitor.h
+// interface/src
+//
+// Created by Ryan Huffman on 8/4/15.
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_ConnectionMonitor_h
+#define hifi_ConnectionMonitor_h
+
+#include
+#include
+
+class QString;
+
+class ConnectionMonitor : public QObject {
+ Q_OBJECT
+public:
+ void init();
+
+private slots:
+ void disconnectedFromDomain();
+ void connectedToDomain(const QString& name);
+ void hostChanged(const QString& name);
+
+private:
+ QTimer _timer;
+};
+
+#endif // hifi_ConnectionMonitor_h
\ No newline at end of file
diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp
index 4051bd8a1e..dd80dadca7 100644
--- a/interface/src/DiscoverabilityManager.cpp
+++ b/interface/src/DiscoverabilityManager.cpp
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
@@ -36,11 +37,11 @@ const QString SESSION_ID_KEY = "session_id";
void DiscoverabilityManager::updateLocation() {
auto accountManager = DependencyManager::get();
-
- if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
- auto addressManager = DependencyManager::get();
- DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler();
+ auto addressManager = DependencyManager::get();
+ auto& domainHandler = DependencyManager::get()->getDomainHandler();
+
+ if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
// construct a QJsonObject given the user's current address information
QJsonObject rootObject;
@@ -48,8 +49,6 @@ void DiscoverabilityManager::updateLocation() {
QString pathString = addressManager->currentPath();
- const QString LOCATION_KEY_IN_ROOT = "location";
-
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
@@ -90,6 +89,7 @@ void DiscoverabilityManager::updateLocation() {
// we have a changed location, send it now
_lastLocationObject = locationObject;
+ const QString LOCATION_KEY_IN_ROOT = "location";
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
apiPath = API_USER_LOCATION_PATH;
@@ -109,6 +109,9 @@ void DiscoverabilityManager::updateLocation() {
accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
QNetworkAccessManager::PutOperation, callbackParameters);
}
+
+ // Update Steam
+ SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingAddress());
}
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 6308ac6c73..50dc748461 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -134,7 +134,7 @@ Menu::Menu() {
// Edit > My Asset Server
auto assetServerAction = addActionToQMenuAndActionHash(editMenu, MenuOption::AssetServer,
Qt::CTRL | Qt::SHIFT | Qt::Key_A,
- qApp, SLOT(toggleAssetServerWidget()));
+ qApp, SLOT(showAssetServerWidget()));
auto nodeList = DependencyManager::get();
QObject::connect(nodeList.data(), &NodeList::canWriteAssetsChanged, assetServerAction, &QAction::setEnabled);
assetServerAction->setEnabled(nodeList->getThisNodeCanWriteAssets());
@@ -337,7 +337,7 @@ Menu::Menu() {
// Developer > Render >>>
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
- addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true);
+ addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DefaultSkybox, 0, true);
// Developer > Render > Throttle FPS If Not Focus
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true);
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 503cbf51fa..d47b6842a5 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -80,6 +80,7 @@ namespace MenuOption {
const QString CrashNewFaultThreaded = "New Fault (threaded)";
const QString DeadlockInterface = "Deadlock Interface";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
+ const QString DefaultSkybox = "Default Skybox";
const QString DeleteBookmark = "Delete Bookmark...";
const QString DisableActivityLogger = "Disable Activity Logger";
const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment";
@@ -175,7 +176,6 @@ namespace MenuOption {
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
const QString SimulateEyeTracking = "Simulate";
const QString SMIEyeTracking = "SMI Eye Tracking";
- const QString Stars = "Stars";
const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp
deleted file mode 100644
index 9510710eb3..0000000000
--- a/interface/src/Stars.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-//
-// Stars.cpp
-// interface/src
-//
-// Created by Tobias Schwinger on 3/22/13.
-// Copyright 2013 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#include "Stars.h"
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-#include
-
-//static const float TILT = 0.23f;
-static const float TILT = 0.0f;
-static const unsigned int STARFIELD_NUM_STARS = 50000;
-static const unsigned int STARFIELD_SEED = 1;
-static const float STAR_COLORIZATION = 0.1f;
-
-static const float TAU = 6.28318530717958f;
-//static const float HALF_TAU = TAU / 2.0f;
-//static const float QUARTER_TAU = TAU / 4.0f;
-//static const float MILKY_WAY_WIDTH = TAU / 30.0f; // width in radians of one half of the Milky Way
-//static const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees
-//static const float MILKY_WAY_RATIO = 0.4f;
-static const char* UNIFORM_TIME_NAME = "iGlobalTime";
-
-// Produce a random float value between 0 and 1
-static float frand() {
- return (float)rand() / (float)RAND_MAX;
-}
-
-// http://mathworld.wolfram.com/SpherePointPicking.html
-static vec2 randPolar() {
- vec2 result(frand(), frand());
- result.x *= TAU;
- result.y = powf(result.y, 2.0) / 2.0f;
- if (frand() > 0.5f) {
- result.y = 0.5f - result.y;
- } else {
- result.y += 0.5f;
- }
- result.y = acos((2.0f * result.y) - 1.0f);
- return result;
-}
-
-
-static vec3 fromPolar(const vec2& polar) {
- float sinTheta = sin(polar.x);
- float cosTheta = cos(polar.x);
- float sinPhi = sin(polar.y);
- float cosPhi = cos(polar.y);
- return vec3(
- cosTheta * sinPhi,
- cosPhi,
- sinTheta * sinPhi);
-}
-
-
-// computeStarColor
-// - Generate a star color.
-//
-// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is.
-//
-// 0 = completely black & white
-// 1 = very colorful
-unsigned computeStarColor(float colorization) {
- unsigned char red, green, blue;
- if (randFloat() < 0.3f) {
- // A few stars are colorful
- red = 2 + (rand() % 254);
- green = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
- blue = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
- } else {
- // Most stars are dimmer and white
- red = green = blue = 2 + (rand() % 128);
- }
- return red | (green << 8) | (blue << 16);
-}
-
-struct StarVertex {
- vec4 position;
- vec4 colorAndSize;
-};
-
-static const int STARS_VERTICES_SLOT{ 0 };
-static const int STARS_COLOR_SLOT{ 1 };
-
-gpu::PipelinePointer Stars::_gridPipeline{};
-gpu::PipelinePointer Stars::_starsPipeline{};
-int32_t Stars::_timeSlot{ -1 };
-
-void Stars::init() {
- if (!_gridPipeline) {
- auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
- auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag));
- auto program = gpu::Shader::createProgram(vs, ps);
- gpu::Shader::makeProgram((*program));
- _timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
- if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
- _timeSlot = program->getUniforms().findLocation(UNIFORM_TIME_NAME);
- }
- auto state = gpu::StatePointer(new gpu::State());
- // enable decal blend
- state->setDepthTest(gpu::State::DepthTest(false));
- state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
- state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
- _gridPipeline = gpu::Pipeline::create(program, state);
- }
-
- if (!_starsPipeline) {
- auto vs = gpu::Shader::createVertex(std::string(stars_vert));
- auto ps = gpu::Shader::createPixel(std::string(stars_frag));
- auto program = gpu::Shader::createProgram(vs, ps);
- gpu::Shader::makeProgram((*program));
- auto state = gpu::StatePointer(new gpu::State());
- // enable decal blend
- state->setDepthTest(gpu::State::DepthTest(false));
- state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
- state->setAntialiasedLineEnable(true); // line smoothing also smooth points
- state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
- _starsPipeline = gpu::Pipeline::create(program, state);
- }
-
- unsigned limit = STARFIELD_NUM_STARS;
- std::vector points;
- points.resize(limit);
-
- { // generate stars
- QElapsedTimer startTime;
- startTime.start();
-
- vertexBuffer.reset(new gpu::Buffer);
-
- srand(STARFIELD_SEED);
- for (size_t star = 0; star < limit; ++star) {
- points[star].position = vec4(fromPolar(randPolar()), 1);
- float size = frand() * 2.5f + 0.5f;
- if (frand() < STAR_COLORIZATION) {
- vec3 color(frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f);
- points[star].colorAndSize = vec4(color, size);
- } else {
- vec3 color(frand() / 2.0f + 0.5f);
- points[star].colorAndSize = vec4(color, size);
- }
- }
-
- double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
- qDebug() << "Total time to generate stars: " << timeDiff << " msec";
- }
-
- gpu::Element positionElement, colorElement;
- const size_t VERTEX_STRIDE = sizeof(StarVertex);
-
- vertexBuffer->append(VERTEX_STRIDE * limit, (const gpu::Byte*)&points[0]);
- streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
- streamFormat->setAttribute(gpu::Stream::POSITION, STARS_VERTICES_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), 0);
- streamFormat->setAttribute(gpu::Stream::COLOR, STARS_COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
- positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
- colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
-
- size_t offset = offsetof(StarVertex, position);
- positionView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement);
-
- offset = offsetof(StarVertex, colorAndSize);
- colorView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, colorElement);
-}
-
-// FIXME star colors
-void Stars::render(RenderArgs* renderArgs, float alpha) {
- std::call_once(once, [&]{ init(); });
-
-
- auto modelCache = DependencyManager::get();
- auto textureCache = DependencyManager::get();
- auto geometryCache = DependencyManager::get();
-
-
- gpu::Batch& batch = *renderArgs->_batch;
- batch.setViewTransform(Transform());
- batch.setProjectionTransform(renderArgs->getViewFrustum().getProjection());
- batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->getViewFrustum().getOrientation()) *
- quat(vec3(TILT, 0, 0))));
- batch.setResourceTexture(0, textureCache->getWhiteTexture());
-
- // Render the world lines
- batch.setPipeline(_gridPipeline);
- static auto start = usecTimestampNow();
- float msecs = (float)(usecTimestampNow() - start) / (float)USECS_PER_MSEC;
- float secs = msecs / (float)MSECS_PER_SECOND;
- batch._glUniform1f(_timeSlot, secs);
- geometryCache->renderCube(batch);
-
- // Render the stars
- batch.setPipeline(_starsPipeline);
- batch.setInputFormat(streamFormat);
- batch.setInputBuffer(STARS_VERTICES_SLOT, positionView);
- batch.setInputBuffer(STARS_COLOR_SLOT, colorView);
- batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
-}
diff --git a/interface/src/Stars.h b/interface/src/Stars.h
deleted file mode 100644
index f07caff770..0000000000
--- a/interface/src/Stars.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// Stars.h
-// interface/src
-//
-// Created by Tobias Schwinger on 3/22/13.
-// Copyright 2013 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_Stars_h
-#define hifi_Stars_h
-
-#include
-
-class RenderArgs;
-
-// Starfield rendering component.
-class Stars {
-public:
- Stars() = default;
- ~Stars() = default;
-
- Stars(Stars const&) = delete;
- Stars& operator=(Stars const&) = delete;
-
- // Renders the starfield from a local viewer's perspective.
- // The parameters specifiy the field of view.
- void render(RenderArgs* args, float alpha);
-
-private:
- // Pipelines
- static gpu::PipelinePointer _gridPipeline;
- static gpu::PipelinePointer _starsPipeline;
- static int32_t _timeSlot;
-
- // Buffers
- gpu::BufferPointer vertexBuffer;
- gpu::Stream::FormatPointer streamFormat;
- gpu::BufferView positionView;
- gpu::BufferView colorView;
- std::once_flag once;
-
- void init();
-};
-
-
-#endif // hifi_Stars_h
diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp
index d92c5a2fda..1946d216ff 100644
--- a/interface/src/audio/AudioScope.cpp
+++ b/interface/src/audio/AudioScope.cpp
@@ -142,7 +142,7 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
mat4 legacyProjection = glm::ortho(0, width, height, 0, -1000, 1000);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
- batch.setViewTransform(Transform());
+ batch.resetViewTransform();
geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground);
renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 4d9481f002..d3a38271a0 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -98,6 +98,7 @@ Avatar::Avatar(RigPointer rig) :
_headData = static_cast(new Head(this));
_skeletonModel = std::make_shared(this, nullptr, rig);
+ connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
}
Avatar::~Avatar() {
@@ -298,7 +299,9 @@ void Avatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("head");
glm::vec3 headPosition = getPosition();
- _skeletonModel->getHeadPosition(headPosition);
+ if (!_skeletonModel->getHeadPosition(headPosition)) {
+ headPosition = getPosition();
+ }
Head* head = getHead();
head->setPosition(headPosition);
head->setScale(getUniformScale());
@@ -306,6 +309,7 @@ void Avatar::simulate(float deltaTime) {
}
} else {
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
+ getHead()->setPosition(getPosition());
_skeletonModel->simulate(deltaTime, false);
}
@@ -776,7 +780,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
{
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect");
- DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true);
+ DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, true);
DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height,
bevelDistance, backgroundColor);
}
@@ -916,6 +920,17 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
}
}
+void Avatar::setModelURLFinished(bool success) {
+ if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
+ qDebug() << "Using default after failing to load Avatar model: " << _skeletonModelURL;
+ // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
+ // we don't redo this every time we receive an identity packet from the avatar with the bad url.
+ QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
+ Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl()));
+ }
+}
+
+
// create new model, can return an instance of a SoftAttachmentModel rather then Model
static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride) {
if (isSoft) {
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index b9f44613c7..910f2cc1e6 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -184,6 +184,8 @@ public slots:
glm::vec3 getRightPalmPosition() const;
glm::quat getRightPalmRotation() const;
+ void setModelURLFinished(bool success);
+
protected:
friend class AvatarManager;
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index eef176338f..782ecbcc64 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -122,11 +122,13 @@ MyAvatar::MyAvatar(RigPointer rig) :
_driveKeys[i] = 0.0f;
}
+
+ // Necessary to select the correct slot
+ using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool);
+
// connect to AddressManager signal for location jumps
- connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired,
- [=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){
- goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation);
- });
+ connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired,
+ this, static_cast(&MyAvatar::goToLocation));
_characterController.setEnabled(true);
@@ -430,6 +432,7 @@ void MyAvatar::simulate(float deltaTime) {
if (!_skeletonModel->hasSkeleton()) {
// All the simulation that can be done has been done
+ getHead()->setPosition(getPosition()); // so audio-position isn't 0,0,0
return;
}
@@ -513,13 +516,23 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
return _sensorToWorldMatrixCache.get();
}
+// As far as I know no HMD system supports a play area of a kilometer in radius.
+static const float MAX_HMD_ORIGIN_DISTANCE = 1000.0f;
// Pass a recent sample of the HMD to the avatar.
// This can also update the avatar's position to follow the HMD
// as it moves through the world.
void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
// update the sensorMatrices based on the new hmd pose
_hmdSensorMatrix = hmdSensorMatrix;
- _hmdSensorPosition = extractTranslation(hmdSensorMatrix);
+ auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix);
+
+ if (newHmdSensorPosition != _hmdSensorPosition &&
+ glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) {
+ qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition;
+ // Ignore unreasonable HMD sensor data
+ return;
+ }
+ _hmdSensorPosition = newHmdSensorPosition;
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
}
@@ -1858,7 +1871,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation);
if (shouldFaceLocation) {
- quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
+ quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP);
// move the user a couple units away
const float DISTANCE_TO_USER = 2.0f;
diff --git a/interface/src/main.cpp b/interface/src/main.cpp
index 8fc0384aee..527b7f2331 100644
--- a/interface/src/main.cpp
+++ b/interface/src/main.cpp
@@ -8,6 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
+
#include
#include
#include
@@ -20,12 +22,13 @@
#include
#include
+#include
+
#include "AddressManager.h"
#include "Application.h"
#include "InterfaceLogging.h"
#include "UserActivityLogger.h"
#include "MainWindow.h"
-#include
#ifdef HAS_BUGSPLAT
#include
@@ -137,6 +140,8 @@ int main(int argc, const char* argv[]) {
// or in the main window ctor, before GL startup.
Application::initPlugins(arguments);
+ SteamClient::init();
+
int exitCode;
{
QSettings::setDefaultFormat(QSettings::IniFormat);
@@ -202,6 +207,8 @@ int main(int argc, const char* argv[]) {
Application::shutdownPlugins();
+ SteamClient::shutdown();
+
qCDebug(interfaceapp, "Normal exit.");
#if !defined(DEBUG) && !defined(Q_OS_LINUX)
// HACK: exit immediately (don't handle shutdown callbacks) for Release build
diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp
index 1328197195..4090c99ac8 100644
--- a/interface/src/scripting/AccountScriptingInterface.cpp
+++ b/interface/src/scripting/AccountScriptingInterface.cpp
@@ -15,6 +15,9 @@
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
static AccountScriptingInterface sharedInstance;
+ auto accountManager = DependencyManager::get();
+ QObject::connect(accountManager.data(), &AccountManager::profileChanged,
+ &sharedInstance, &AccountScriptingInterface::usernameChanged);
return &sharedInstance;
}
diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h
index 888149b836..49648781ce 100644
--- a/interface/src/scripting/AccountScriptingInterface.h
+++ b/interface/src/scripting/AccountScriptingInterface.h
@@ -17,6 +17,11 @@
class AccountScriptingInterface : public QObject {
Q_OBJECT
+ Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
+
+signals:
+ void usernameChanged();
+
public slots:
static AccountScriptingInterface* getInstance();
QString getUsername();
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index b165cda135..9d7eee0f8c 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -179,6 +179,10 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString&
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
}
+void WindowScriptingInterface::showAssetServer(const QString& upload) {
+ QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload));
+}
+
int WindowScriptingInterface::getInnerWidth() {
return qApp->getWindow()->geometry().width();
}
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index 9d73111333..9f1d2bddf5 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -53,6 +53,7 @@ public slots:
CustomPromptResult customPrompt(const QVariant& config);
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
+ void showAssetServer(const QString& upload = "");
void copyToClipboard(const QString& text);
signals:
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 6d5df31766..197fb5b58d 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -28,6 +28,8 @@
#include "Util.h"
#include "ui/Stats.h"
#include "ui/AvatarInputs.h"
+#include "OffscreenUi.h"
+#include
const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
static const float ORTHO_NEAR_CLIP = -1000.0f;
@@ -65,7 +67,9 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
// Execute the batch into our framebuffer
doInBatch(renderArgs->_context, [&](gpu::Batch& batch) {
+ PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender");
renderArgs->_batch = &batch;
+ batch.enableStereo(false);
int width = _overlayFramebuffer->getWidth();
int height = _overlayFramebuffer->getHeight();
@@ -99,7 +103,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
geometryCache->useSimpleDrawPipeline(batch);
batch.setProjectionTransform(mat4());
batch.setModelTransform(Transform());
- batch.setViewTransform(Transform());
+ batch.resetViewTransform();
batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _uiTexture);
geometryCache->renderUnitQuad(batch, glm::vec4(1));
@@ -119,7 +123,7 @@ void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) {
mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
- batch.setViewTransform(Transform());
+ batch.resetViewTransform();
// Render the audio scope
DependencyManager::get()->render(renderArgs, width, height);
@@ -138,7 +142,7 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
- batch.setViewTransform(Transform());
+ batch.resetViewTransform();
// Render all of the Script based "HUD" aka 2D overlays.
// note: we call them HUD, as opposed to 2D, only because there are some cases of 3D HUD overlays, like the
@@ -164,7 +168,7 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
- batch.setViewTransform(Transform());
+ batch.resetViewTransform();
float screenRatio = ((float)qApp->getDevicePixelRatio());
float renderRatio = ((float)qApp->getRenderResolutionScale());
@@ -177,13 +181,11 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
glm::vec2 texCoordMinCorner(0.0f, 0.0f);
glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight()));
-
- geometryCache->useSimpleDrawPipeline(batch, true);
batch.setResourceTexture(0, selfieTexture);
- geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
+ float alpha = DependencyManager::get()->getDesktop()->property("unpinnedAlpha").toFloat();
+ geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
batch.setResourceTexture(0, renderArgs->_whiteTexture);
- geometryCache->useSimpleDrawPipeline(batch, false);
}
}
@@ -228,7 +230,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
geometryCache->useSimpleDrawPipeline(batch);
batch.setProjectionTransform(mat4());
batch.setModelTransform(Transform());
- batch.setViewTransform(Transform());
+ batch.resetViewTransform();
batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture());
// FIXME: THe line width of CONNECTION_STATUS_BORDER_LINE_WIDTH is not supported anymore, we ll need a workaround
@@ -246,10 +248,6 @@ static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)
static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
-std::mutex _textureGuard;
-using Lock = std::unique_lock;
-std::queue _availableTextures;
-
void ApplicationOverlay::buildFramebufferObject() {
PROFILE_RANGE(__FUNCTION__);
@@ -265,22 +263,6 @@ void ApplicationOverlay::buildFramebufferObject() {
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
}
- if (!_overlayFramebuffer->getRenderBuffer(0)) {
- gpu::TexturePointer newColorAttachment;
- {
- Lock lock(_textureGuard);
- if (!_availableTextures.empty()) {
- newColorAttachment = _availableTextures.front();
- _availableTextures.pop();
- }
- }
- if (newColorAttachment) {
- newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples());
- _overlayFramebuffer->setRenderBuffer(0, newColorAttachment);
- }
- }
-
- // If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one
if (!_overlayFramebuffer->getRenderBuffer(0)) {
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
@@ -288,20 +270,9 @@ void ApplicationOverlay::buildFramebufferObject() {
}
}
-gpu::TexturePointer ApplicationOverlay::acquireOverlay() {
+gpu::TexturePointer ApplicationOverlay::getOverlayTexture() {
if (!_overlayFramebuffer) {
return gpu::TexturePointer();
}
- auto result = _overlayFramebuffer->getRenderBuffer(0);
- _overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer());
- return result;
-}
-
-void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) {
- if (texture) {
- Lock lock(_textureGuard);
- _availableTextures.push(texture);
- } else {
- qWarning() << "Attempted to release null texture";
- }
-}
+ return _overlayFramebuffer->getRenderBuffer(0);
+}
\ No newline at end of file
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index b77fcc6f89..b7a0529f92 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -26,8 +26,7 @@ public:
void renderOverlay(RenderArgs* renderArgs);
- gpu::TexturePointer acquireOverlay();
- void releaseOverlay(gpu::TexturePointer pointer);
+ gpu::TexturePointer getOverlayTexture();
private:
void renderStatsAndLogs(RenderArgs* renderArgs);
diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp
index 2f826146ae..dc06c50626 100644
--- a/interface/src/ui/DialogsManager.cpp
+++ b/interface/src/ui/DialogsManager.cpp
@@ -50,6 +50,10 @@ void DialogsManager::toggleAddressBar() {
emit addressBarToggled();
}
+void DialogsManager::showAddressBar() {
+ AddressBarDialog::show();
+}
+
void DialogsManager::toggleDiskCacheEditor() {
maybeCreateDialog(_diskCacheEditor);
_diskCacheEditor->toggle();
diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h
index c48c6df0e6..5b4995029f 100644
--- a/interface/src/ui/DialogsManager.h
+++ b/interface/src/ui/DialogsManager.h
@@ -44,6 +44,7 @@ public:
public slots:
void toggleAddressBar();
+ void showAddressBar();
void toggleDiskCacheEditor();
void toggleLoginDialog();
void showLoginDialog();
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
index 80d52b7a07..fef0a12813 100644
--- a/interface/src/ui/LoginDialog.cpp
+++ b/interface/src/ui/LoginDialog.cpp
@@ -12,8 +12,11 @@
#include "LoginDialog.h"
#include
+#include
+#include
#include
+#include
#include "AccountManager.h"
#include "DependencyManager.h"
@@ -21,9 +24,7 @@
HIFI_QML_DEF(LoginDialog)
-LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent),
- _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString())
-{
+LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
auto accountManager = DependencyManager::get();
connect(accountManager.data(), &AccountManager::loginComplete,
this, &LoginDialog::handleLoginCompleted);
@@ -53,36 +54,102 @@ void LoginDialog::toggleAction() {
}
}
-void LoginDialog::handleLoginCompleted(const QUrl&) {
- hide();
+bool LoginDialog::isSteamRunning() const {
+ return SteamClient::isRunning();
}
-void LoginDialog::handleLoginFailed() {
- setStatusText("Invalid username or password");
-}
-
-void LoginDialog::setStatusText(const QString& statusText) {
- if (statusText != _statusText) {
- _statusText = statusText;
- emit statusTextChanged();
- }
-}
-
-QString LoginDialog::statusText() const {
- return _statusText;
-}
-
-QString LoginDialog::rootUrl() const {
- return _rootUrl;
-}
-
-void LoginDialog::login(const QString& username, const QString& password) {
+void LoginDialog::login(const QString& username, const QString& password) const {
qDebug() << "Attempting to login " << username;
- setStatusText("Logging in...");
DependencyManager::get()->requestAccessToken(username, password);
}
-void LoginDialog::openUrl(const QString& url) {
- qDebug() << url;
- QDesktopServices::openUrl(url);
+void LoginDialog::loginThroughSteam() {
+ qDebug() << "Attempting to login through Steam";
+ SteamClient::requestTicket([this](Ticket ticket) {
+ if (ticket.isNull()) {
+ emit handleLoginFailed();
+ return;
+ }
+
+ DependencyManager::get()->requestAccessTokenWithSteam(ticket);
+ });
}
+
+void LoginDialog::linkSteam() {
+ qDebug() << "Attempting to link Steam account";
+ SteamClient::requestTicket([this](Ticket ticket) {
+ if (ticket.isNull()) {
+ emit handleLoginFailed();
+ return;
+ }
+
+ JSONCallbackParameters callbackParams;
+ callbackParams.jsonCallbackReceiver = this;
+ callbackParams.jsonCallbackMethod = "linkCompleted";
+ callbackParams.errorCallbackReceiver = this;
+ callbackParams.errorCallbackMethod = "linkFailed";
+
+ const QString LINK_STEAM_PATH = "api/v1/user/steam/link";
+
+ QJsonObject payload;
+ payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
+
+ auto accountManager = DependencyManager::get();
+ accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required,
+ QNetworkAccessManager::PostOperation, callbackParams,
+ QJsonDocument(payload).toJson());
+ });
+}
+
+void LoginDialog::createAccountFromStream(QString username) {
+ qDebug() << "Attempting to create account from Steam info";
+ SteamClient::requestTicket([this, username](Ticket ticket) {
+ if (ticket.isNull()) {
+ emit handleLoginFailed();
+ return;
+ }
+
+ JSONCallbackParameters callbackParams;
+ callbackParams.jsonCallbackReceiver = this;
+ callbackParams.jsonCallbackMethod = "createCompleted";
+ callbackParams.errorCallbackReceiver = this;
+ callbackParams.errorCallbackMethod = "createFailed";
+
+ const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create";
+
+ QJsonObject payload;
+ payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
+ if (!username.isEmpty()) {
+ payload.insert("username", QJsonValue::fromVariant(QVariant(username)));
+ }
+
+ auto accountManager = DependencyManager::get();
+ accountManager->sendRequest(CREATE_ACCOUNT_FROM_STEAM_PATH, AccountManagerAuth::None,
+ QNetworkAccessManager::PostOperation, callbackParams,
+ QJsonDocument(payload).toJson());
+ });
+
+}
+
+void LoginDialog::openUrl(const QString& url) const {
+ auto offscreenUi = DependencyManager::get();
+ auto browser = offscreenUi->load("Browser.qml");
+ browser->setProperty("url", url);
+}
+
+void LoginDialog::linkCompleted(QNetworkReply& reply) {
+ emit handleLinkCompleted();
+}
+
+void LoginDialog::linkFailed(QNetworkReply& reply) {
+ emit handleLinkFailed(reply.errorString());
+}
+
+void LoginDialog::createCompleted(QNetworkReply& reply) {
+ emit handleCreateCompleted();
+}
+
+void LoginDialog::createFailed(QNetworkReply& reply) {
+ emit handleCreateFailed(reply.errorString());
+}
+
diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h
index 25ecf45898..8b6dc40302 100644
--- a/interface/src/ui/LoginDialog.h
+++ b/interface/src/ui/LoginDialog.h
@@ -16,35 +16,44 @@
#include
+class QNetworkReply;
+
class LoginDialog : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL
- Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged)
- Q_PROPERTY(QString rootUrl READ rootUrl)
-
public:
static void toggleAction();
LoginDialog(QQuickItem* parent = nullptr);
- void setStatusText(const QString& statusText);
- QString statusText() const;
-
- QString rootUrl() const;
-
signals:
- void statusTextChanged();
-
-protected:
- void handleLoginCompleted(const QUrl& authURL);
+ void handleLoginCompleted();
void handleLoginFailed();
- Q_INVOKABLE void login(const QString& username, const QString& password);
- Q_INVOKABLE void openUrl(const QString& url);
-private:
- QString _statusText;
- const QString _rootUrl;
+ void handleLinkCompleted();
+ void handleLinkFailed(QString error);
+
+ void handleCreateCompleted();
+ void handleCreateFailed(QString error);
+
+public slots:
+ void linkCompleted(QNetworkReply& reply);
+ void linkFailed(QNetworkReply& reply);
+
+ void createCompleted(QNetworkReply& reply);
+ void createFailed(QNetworkReply& reply);
+
+protected slots:
+ Q_INVOKABLE bool isSteamRunning() const;
+
+ Q_INVOKABLE void login(const QString& username, const QString& password) const;
+ Q_INVOKABLE void loginThroughSteam();
+ Q_INVOKABLE void linkSteam();
+ Q_INVOKABLE void createAccountFromStream(QString username = QString());
+
+ Q_INVOKABLE void openUrl(const QString& url) const;
+
};
#endif // hifi_LoginDialog_h
diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index ec4b2280b6..7fdf5cd57d 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -136,6 +136,9 @@ void Stats::updateStats(bool force) {
STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f);
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
+ STAT_UPDATE_FLOAT(assetMbpsIn, (float)bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AssetServer) / 1000.0f, 0.01f);
+ STAT_UPDATE_FLOAT(assetMbpsOut, (float)bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AssetServer) / 1000.0f, 0.01f);
+
// Second column: ping
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h
index f6643a1a7a..4be2d88d9e 100644
--- a/interface/src/ui/Stats.h
+++ b/interface/src/ui/Stats.h
@@ -43,6 +43,8 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, packetOutCount, 0)
STATS_PROPERTY(float, mbpsIn, 0)
STATS_PROPERTY(float, mbpsOut, 0)
+ STATS_PROPERTY(float, assetMbpsIn, 0)
+ STATS_PROPERTY(float, assetMbpsOut, 0)
STATS_PROPERTY(int, audioPing, 0)
STATS_PROPERTY(int, avatarPing, 0)
STATS_PROPERTY(int, entitiesPing, 0)
@@ -128,6 +130,8 @@ signals:
void packetOutCountChanged();
void mbpsInChanged();
void mbpsOutChanged();
+ void assetMbpsInChanged();
+ void assetMbpsOutChanged();
void audioPingChanged();
void avatarPingChanged();
void entitiesPingChanged();
diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp
index 94533137ad..8f2149f02d 100644
--- a/interface/src/ui/overlays/Base3DOverlay.cpp
+++ b/interface/src/ui/overlays/Base3DOverlay.cpp
@@ -21,6 +21,7 @@ const bool DEFAULT_IS_SOLID = false;
const bool DEFAULT_IS_DASHED_LINE = false;
Base3DOverlay::Base3DOverlay() :
+ SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
_lineWidth(DEFAULT_LINE_WIDTH),
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
@@ -31,15 +32,85 @@ Base3DOverlay::Base3DOverlay() :
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
Overlay(base3DOverlay),
- _transform(base3DOverlay->_transform),
+ SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
_lineWidth(base3DOverlay->_lineWidth),
_isSolid(base3DOverlay->_isSolid),
_isDashedLine(base3DOverlay->_isDashedLine),
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
_drawInFront(base3DOverlay->_drawInFront)
{
+ setTransform(base3DOverlay->getTransform());
}
-void Base3DOverlay::setProperties(const QVariantMap& properties) {
+
+QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties) {
+ // the position and rotation in _transform are relative to the parent (aka local). The versions coming from
+ // scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties
+ // so that "position" and "rotation" are relative-to-parent values.
+ QVariantMap result = properties;
+ QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : QUuid();
+ int parentJointIndex = result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : -1;
+ bool success;
+
+ // make "position" and "orientation" be relative-to-parent
+ if (result["localPosition"].isValid()) {
+ result["position"] = result["localPosition"];
+ } else if (result["position"].isValid()) {
+ glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]),
+ parentID, parentJointIndex, success);
+ result["position"] = vec3toVariant(localPosition);
+ }
+
+ if (result["localOrientation"].isValid()) {
+ result["orientation"] = result["localOrientation"];
+ } else if (result["orientation"].isValid()) {
+ glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]),
+ parentID, parentJointIndex, success);
+ result["orientation"] = quatToVariant(localOrientation);
+ }
+
+ return result;
+}
+
+void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
+ QVariantMap properties = originalProperties;
+
+ // carry over some legacy keys
+ if (!properties["position"].isValid() && !properties["localPosition"].isValid()) {
+ if (properties["p1"].isValid()) {
+ properties["position"] = properties["p1"];
+ } else if (properties["point"].isValid()) {
+ properties["position"] = properties["point"];
+ } else if (properties["start"].isValid()) {
+ properties["position"] = properties["start"];
+ }
+ }
+ if (!properties["orientation"].isValid() && properties["rotation"].isValid()) {
+ properties["orientation"] = properties["rotation"];
+ }
+ if (!properties["localOrientation"].isValid() && properties["localRotation"].isValid()) {
+ properties["localOrientation"] = properties["localRotation"];
+ }
+
+ // All of parentID, parentJointIndex, position, orientation are needed to make sense of any of them.
+ // If any of these changed, pull any missing properties from the overlay.
+ if (properties["parentID"].isValid() || properties["parentJointIndex"].isValid() ||
+ properties["position"].isValid() || properties["localPosition"].isValid() ||
+ properties["orientation"].isValid() || properties["localOrientation"].isValid()) {
+ if (!properties["parentID"].isValid()) {
+ properties["parentID"] = getParentID();
+ }
+ if (!properties["parentJointIndex"].isValid()) {
+ properties["parentJointIndex"] = getParentJointIndex();
+ }
+ if (!properties["position"].isValid() && !properties["localPosition"].isValid()) {
+ properties["position"] = vec3toVariant(getPosition());
+ }
+ if (!properties["orientation"].isValid() && !properties["localOrientation"].isValid()) {
+ properties["orientation"] = quatToVariant(getOrientation());
+ }
+ }
+
+ properties = convertOverlayLocationFromScriptSemantics(properties);
Overlay::setProperties(properties);
bool needRenderItemUpdate = false;
@@ -52,17 +123,12 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
needRenderItemUpdate = true;
}
- auto position = properties["position"];
-
- // if "position" property was not there, check to see if they included aliases: point, p1
- if (!position.isValid()) {
- position = properties["p1"];
- if (!position.isValid()) {
- position = properties["point"];
- }
+ if (properties["position"].isValid()) {
+ setLocalPosition(vec3FromVariant(properties["position"]));
+ needRenderItemUpdate = true;
}
- if (position.isValid()) {
- setPosition(vec3FromVariant(position));
+ if (properties["orientation"].isValid()) {
+ setLocalOrientation(quatFromVariant(properties["orientation"]));
needRenderItemUpdate = true;
}
@@ -71,13 +137,6 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
needRenderItemUpdate = true;
}
- auto rotation = properties["rotation"];
-
- if (rotation.isValid()) {
- setRotation(quatFromVariant(rotation));
- needRenderItemUpdate = true;
- }
-
if (properties["isSolid"].isValid()) {
setIsSolid(properties["isSolid"].toBool());
}
@@ -107,6 +166,15 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool());
}
+ if (properties["parentID"].isValid()) {
+ setParentID(QUuid(properties["parentID"].toString()));
+ needRenderItemUpdate = true;
+ }
+ if (properties["parentJointIndex"].isValid()) {
+ setParentJointIndex(properties["parentJointIndex"].toInt());
+ needRenderItemUpdate = true;
+ }
+
// Communicate changes to the renderItem if needed
if (needRenderItemUpdate) {
auto itemID = getRenderItemID();
@@ -123,12 +191,18 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "position" || property == "start" || property == "p1" || property == "point") {
return vec3toVariant(getPosition());
}
+ if (property == "localPosition") {
+ return vec3toVariant(getLocalPosition());
+ }
+ if (property == "rotation" || property == "orientation") {
+ return quatToVariant(getOrientation());
+ }
+ if (property == "localRotation" || property == "localOrientation") {
+ return quatToVariant(getLocalOrientation());
+ }
if (property == "lineWidth") {
return _lineWidth;
}
- if (property == "rotation") {
- return quatToVariant(getRotation());
- }
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") {
return _isSolid;
}
@@ -144,6 +218,12 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "drawInFront") {
return _drawInFront;
}
+ if (property == "parentID") {
+ return getParentID();
+ }
+ if (property == "parentJointIndex") {
+ return getParentJointIndex();
+ }
return Overlay::getProperty(property);
}
@@ -152,3 +232,19 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
return false;
}
+
+void Base3DOverlay::locationChanged(bool tellPhysics) {
+ auto itemID = getRenderItemID();
+ if (render::Item::isValidID(itemID)) {
+ render::ScenePointer scene = qApp->getMain3DScene();
+ render::PendingChanges pendingChanges;
+ pendingChanges.updateItem(itemID);
+ scene->enqueuePendingChanges(pendingChanges);
+ }
+ // Overlays can't currently have children.
+ // SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
+}
+
+void Base3DOverlay::parentDeleted() {
+ qApp->getOverlays().deleteOverlay(getOverlayID());
+}
diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h
index e602dec48c..1860af4e85 100644
--- a/interface/src/ui/overlays/Base3DOverlay.h
+++ b/interface/src/ui/overlays/Base3DOverlay.h
@@ -12,10 +12,11 @@
#define hifi_Base3DOverlay_h
#include
+#include
#include "Overlay.h"
-class Base3DOverlay : public Overlay {
+class Base3DOverlay : public Overlay, public SpatiallyNestable {
Q_OBJECT
public:
@@ -24,12 +25,9 @@ public:
// getters
virtual bool is3D() const override { return true; }
- const glm::vec3& getPosition() const { return _transform.getTranslation(); }
- const glm::quat& getRotation() const { return _transform.getRotation(); }
- const glm::vec3& getScale() const { return _transform.getScale(); }
// TODO: consider implementing registration points in this class
- const glm::vec3& getCenter() const { return getPosition(); }
+ glm::vec3 getCenter() const { return getPosition(); }
float getLineWidth() const { return _lineWidth; }
bool getIsSolid() const { return _isSolid; }
@@ -38,12 +36,6 @@ public:
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
bool getDrawInFront() const { return _drawInFront; }
- // setters
- void setPosition(const glm::vec3& value) { _transform.setTranslation(value); }
- void setRotation(const glm::quat& value) { _transform.setRotation(value); }
- void setScale(float value) { _transform.setScale(value); }
- void setScale(const glm::vec3& value) { _transform.setScale(value); }
-
void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
@@ -64,7 +56,8 @@ public:
}
protected:
- Transform _transform;
+ virtual void locationChanged(bool tellPhysics = true) override;
+ virtual void parentDeleted() override;
float _lineWidth;
bool _isSolid;
diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp
index 6ebfd5c71c..e9ee997aac 100644
--- a/interface/src/ui/overlays/Circle3DOverlay.cpp
+++ b/interface/src/ui/overlays/Circle3DOverlay.cpp
@@ -69,17 +69,17 @@ void Circle3DOverlay::render(RenderArgs* args) {
// FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround
- auto transform = _transform;
+ auto transform = getTransform();
transform.postScale(glm::vec3(getDimensions(), 1.0f));
batch.setModelTransform(transform);
-
+
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
// we just draw a line...
if (getIsSolid()) {
if (!_quadVerticesID) {
_quadVerticesID = geometryCache->allocateID();
}
-
+
if (geometryChanged) {
QVector points;
QVector colors;
diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp
index f72fb8d920..a61e442436 100644
--- a/interface/src/ui/overlays/Cube3DOverlay.cpp
+++ b/interface/src/ui/overlays/Cube3DOverlay.cpp
@@ -47,7 +47,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
auto geometryCache = DependencyManager::get();
auto pipeline = args->_pipeline;
if (!pipeline) {
- pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
+ pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
}
if (_isSolid) {
@@ -55,7 +55,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
batch->setModelTransform(transform);
geometryCache->renderSolidCubeInstance(*batch, cubeColor, pipeline);
} else {
- geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+ geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
if (getIsDashedLine()) {
transform.setScale(1.0f);
batch->setModelTransform(transform);
diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp
index d59e552779..a384c992ad 100644
--- a/interface/src/ui/overlays/Image3DOverlay.cpp
+++ b/interface/src/ui/overlays/Image3DOverlay.cpp
@@ -37,7 +37,11 @@ Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) :
}
void Image3DOverlay::update(float deltatime) {
- applyTransformTo(_transform);
+ if (usecTimestampNow() > _transformExpiry) {
+ Transform transform = getTransform();
+ applyTransformTo(transform);
+ setTransform(transform);
+ }
}
void Image3DOverlay::render(RenderArgs* args) {
@@ -86,13 +90,14 @@ void Image3DOverlay::render(RenderArgs* args) {
xColor color = getColor();
float alpha = getAlpha();
- applyTransformTo(_transform, true);
- Transform transform = _transform;
+ Transform transform = getTransform();
+ applyTransformTo(transform, true);
+ setTransform(transform);
transform.postScale(glm::vec3(getDimensions(), 1.0f));
batch->setModelTransform(transform);
batch->setResourceTexture(0, _texture->getGPUTexture());
-
+
DependencyManager::get()->renderQuad(
*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)
@@ -187,7 +192,10 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
if (_texture && _texture->isLoaded()) {
// Make sure position and rotation is updated.
- applyTransformTo(_transform, true);
+ Transform transform = getTransform();
+ // XXX this code runs too often for this...
+ // applyTransformTo(transform, true);
+ // setTransform(transform);
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
bool isNull = _fromImage.isNull();
@@ -197,7 +205,10 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
// FIXME - face and surfaceNormal not being set
- return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance);
+ return findRayRectangleIntersection(origin, direction,
+ transform.getRotation(),
+ transform.getTranslation(),
+ dimensions, distance);
}
return false;
diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp
index c9a8b19f6a..c3a6c5920e 100644
--- a/interface/src/ui/overlays/Line3DOverlay.cpp
+++ b/interface/src/ui/overlays/Line3DOverlay.cpp
@@ -23,6 +23,7 @@ Line3DOverlay::Line3DOverlay() :
Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
Base3DOverlay(line3DOverlay),
+ _start(line3DOverlay->_start),
_end(line3DOverlay->_end),
_geometryCacheID(DependencyManager::get()->allocateID())
{
@@ -31,12 +32,46 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
Line3DOverlay::~Line3DOverlay() {
}
+glm::vec3 Line3DOverlay::getStart() const {
+ bool success;
+ glm::vec3 worldStart = localToWorld(_start, _parentID, _parentJointIndex, success);
+ if (!success) {
+ qDebug() << "Line3DOverlay::getStart failed";
+ }
+ return worldStart;
+}
+
+glm::vec3 Line3DOverlay::getEnd() const {
+ bool success;
+ glm::vec3 worldEnd = localToWorld(_end, _parentID, _parentJointIndex, success);
+ if (!success) {
+ qDebug() << "Line3DOverlay::getEnd failed";
+ }
+ return worldEnd;
+}
+
+void Line3DOverlay::setStart(const glm::vec3& start) {
+ bool success;
+ _start = worldToLocal(start, _parentID, _parentJointIndex, success);
+ if (!success) {
+ qDebug() << "Line3DOverlay::setStart failed";
+ }
+}
+
+void Line3DOverlay::setEnd(const glm::vec3& end) {
+ bool success;
+ _end = worldToLocal(end, _parentID, _parentJointIndex, success);
+ if (!success) {
+ qDebug() << "Line3DOverlay::setEnd failed";
+ }
+}
+
AABox Line3DOverlay::getBounds() const {
auto extents = Extents{};
extents.addPoint(_start);
extents.addPoint(_end);
- extents.transform(_transform);
-
+ extents.transform(getTransform());
+
return AABox(extents);
}
@@ -52,17 +87,17 @@ void Line3DOverlay::render(RenderArgs* args) {
auto batch = args->_batch;
if (batch) {
- batch->setModelTransform(_transform);
+ batch->setModelTransform(getTransform());
auto geometryCache = DependencyManager::get();
if (getIsDashedLine()) {
// TODO: add support for color to renderDashedLine()
- geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+ geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
geometryCache->renderDashedLine(*batch, _start, _end, colorv4, _geometryCacheID);
} else if (_glow > 0.0f) {
geometryCache->renderGlowLine(*batch, _start, _end, colorv4, _glow, _glowWidth, _geometryCacheID);
} else {
- geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+ geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
geometryCache->renderLine(*batch, _start, _end, colorv4, _geometryCacheID);
}
}
@@ -76,8 +111,8 @@ const render::ShapeKey Line3DOverlay::getShapeKey() {
return builder.build();
}
-void Line3DOverlay::setProperties(const QVariantMap& properties) {
- Base3DOverlay::setProperties(properties);
+void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
+ QVariantMap properties = originalProperties;
auto start = properties["start"];
// if "start" property was not there, check to see if they included aliases: startPoint
@@ -87,6 +122,7 @@ void Line3DOverlay::setProperties(const QVariantMap& properties) {
if (start.isValid()) {
setStart(vec3FromVariant(start));
}
+ properties.remove("start"); // so that Base3DOverlay doesn't respond to it
auto end = properties["end"];
// if "end" property was not there, check to see if they included aliases: endPoint
@@ -109,14 +145,16 @@ void Line3DOverlay::setProperties(const QVariantMap& properties) {
if (glowWidth.isValid()) {
setGlow(glowWidth.toFloat());
}
+
+ Base3DOverlay::setProperties(properties);
}
QVariant Line3DOverlay::getProperty(const QString& property) {
if (property == "start" || property == "startPoint" || property == "p1") {
- return vec3toVariant(_start);
+ return vec3toVariant(getStart());
}
if (property == "end" || property == "endPoint" || property == "p2") {
- return vec3toVariant(_end);
+ return vec3toVariant(getEnd());
}
return Base3DOverlay::getProperty(property);
@@ -125,3 +163,8 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
Line3DOverlay* Line3DOverlay::createClone() const {
return new Line3DOverlay(this);
}
+
+
+void Line3DOverlay::locationChanged(bool tellPhysics) {
+ // do nothing
+}
diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h
index d066677c70..b4e2ba8168 100644
--- a/interface/src/ui/overlays/Line3DOverlay.h
+++ b/interface/src/ui/overlays/Line3DOverlay.h
@@ -28,14 +28,15 @@ public:
virtual AABox getBounds() const override;
// getters
- const glm::vec3& getStart() const { return _start; }
- const glm::vec3& getEnd() const { return _end; }
+ glm::vec3 getStart() const;
+ glm::vec3 getEnd() const;
const float& getGlow() const { return _glow; }
const float& getGlowWidth() const { return _glowWidth; }
// setters
- void setStart(const glm::vec3& start) { _start = start; }
- void setEnd(const glm::vec3& end) { _end = end; }
+ void setStart(const glm::vec3& start);
+ void setEnd(const glm::vec3& end);
+
void setGlow(const float& glow) { _glow = glow; }
void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; }
@@ -44,6 +45,8 @@ public:
virtual Line3DOverlay* createClone() const override;
+ virtual void locationChanged(bool tellPhysics = true) override;
+
protected:
glm::vec3 _start;
glm::vec3 _end;
diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp
index 9c203c0129..0a89268f6b 100644
--- a/interface/src/ui/overlays/ModelOverlay.cpp
+++ b/interface/src/ui/overlays/ModelOverlay.cpp
@@ -178,3 +178,12 @@ bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const g
ModelOverlay* ModelOverlay::createClone() const {
return new ModelOverlay(this);
}
+
+void ModelOverlay::locationChanged(bool tellPhysics) {
+ Base3DOverlay::locationChanged(tellPhysics);
+
+ if (_model && _model->isActive()) {
+ _model->setRotation(getRotation());
+ _model->setTranslation(getPosition());
+ }
+}
diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h
index 091cab44c9..d5f709c2db 100644
--- a/interface/src/ui/overlays/ModelOverlay.h
+++ b/interface/src/ui/overlays/ModelOverlay.h
@@ -39,6 +39,8 @@ public:
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) override;
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) override;
+ void locationChanged(bool tellPhysics) override;
+
private:
ModelPointer _model;
diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp
index 5d2c315a92..82b90d228c 100644
--- a/interface/src/ui/overlays/Overlay.cpp
+++ b/interface/src/ui/overlays/Overlay.cpp
@@ -77,7 +77,7 @@ void Overlay::setProperties(const QVariantMap& properties) {
if (properties["pulsePeriod"].isValid()) {
setPulsePeriod(properties["pulsePeriod"].toFloat());
}
-
+
if (properties["alphaPulse"].isValid()) {
setAlphaPulse(properties["alphaPulse"].toFloat());
}
@@ -90,7 +90,7 @@ void Overlay::setProperties(const QVariantMap& properties) {
bool visible = properties["visible"].toBool();
setVisible(visible);
}
-
+
if (properties["anchor"].isValid()) {
QString property = properties["anchor"].toString();
if (property == "MyAvatar") {
diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h
index 466ec0e913..51792b24b3 100644
--- a/interface/src/ui/overlays/Overlay.h
+++ b/interface/src/ui/overlays/Overlay.h
@@ -17,7 +17,7 @@
class Overlay : public QObject {
Q_OBJECT
-
+
public:
enum Anchor {
NO_ANCHOR,
@@ -31,9 +31,13 @@ public:
Overlay();
Overlay(const Overlay* overlay);
~Overlay();
+
+ unsigned int getOverlayID() { return _overlayID; }
+ void setOverlayID(unsigned int overlayID) { _overlayID = overlayID; }
+
virtual void update(float deltatime) {}
virtual void render(RenderArgs* args) = 0;
-
+
virtual AABox getBounds() const = 0;
virtual bool supportsGetProperty() const { return true; }
@@ -85,6 +89,8 @@ protected:
render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID };
+ unsigned int _overlayID; // what Overlays.cpp knows this instance as
+
bool _isLoaded;
float _alpha;
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index 0b4bcc8652..242821234a 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -121,7 +121,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
batch.setResourceTexture(0, textureCache->getWhiteTexture()); // FIXME - do we really need to do this??
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
- batch.setViewTransform(Transform());
+ batch.resetViewTransform();
thisOverlay->render(renderArgs);
}
@@ -192,6 +192,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QVariant& propertie
unsigned int Overlays::addOverlay(Overlay::Pointer overlay) {
QWriteLocker lock(&_lock);
unsigned int thisID = _nextOverlayID;
+ overlay->setOverlayID(thisID);
_nextOverlayID++;
if (overlay->is3D()) {
_overlaysWorld[thisID] = overlay;
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index 99f74fa0f9..e19a6b36a9 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -93,7 +93,7 @@ public slots:
/// successful edit, if the input id is for an unknown overlay this function will have no effect
bool editOverlays(const QVariant& propertiesById);
- /// deletes a particle
+ /// deletes an overlay
void deleteOverlay(unsigned int id);
/// get the string type of the overlay used in addOverlay
diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp
index c580464e16..58d72b100b 100644
--- a/interface/src/ui/overlays/Planar3DOverlay.cpp
+++ b/interface/src/ui/overlays/Planar3DOverlay.cpp
@@ -28,10 +28,10 @@ Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) :
AABox Planar3DOverlay::getBounds() const {
auto halfDimensions = glm::vec3{_dimensions / 2.0f, 0.01f};
-
+
auto extents = Extents{-halfDimensions, halfDimensions};
- extents.transform(_transform);
-
+ extents.transform(getTransform());
+
return AABox(extents);
}
diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
index 75d7ec565c..35c479dce6 100644
--- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp
+++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
@@ -61,7 +61,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
geometryCache->bindSimpleProgram(*batch);
geometryCache->renderQuad(*batch, topLeft, bottomRight, rectangleColor);
} else {
- geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+ geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
if (getIsDashedLine()) {
glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp
index cd07385aab..2556bc84aa 100644
--- a/interface/src/ui/overlays/Shape3DOverlay.cpp
+++ b/interface/src/ui/overlays/Shape3DOverlay.cpp
@@ -47,7 +47,7 @@ void Shape3DOverlay::render(RenderArgs* args) {
auto geometryCache = DependencyManager::get();
auto pipeline = args->_pipeline;
if (!pipeline) {
- pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
+ pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
}
transform.setScale(dimensions);
diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp
index bbdd886d11..07c2861f16 100644
--- a/interface/src/ui/overlays/Sphere3DOverlay.cpp
+++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp
@@ -39,14 +39,14 @@ void Sphere3DOverlay::render(RenderArgs* args) {
auto batch = args->_batch;
if (batch) {
- Transform transform = _transform;
+ Transform transform = getTransform();
transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE);
batch->setModelTransform(transform);
auto geometryCache = DependencyManager::get();
auto pipeline = args->_pipeline;
if (!pipeline) {
- pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
+ pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
}
if (_isSolid) {
diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp
index 0ae1c306ba..153f787fc7 100644
--- a/interface/src/ui/overlays/Text3DOverlay.cpp
+++ b/interface/src/ui/overlays/Text3DOverlay.cpp
@@ -65,44 +65,49 @@ xColor Text3DOverlay::getBackgroundColor() {
}
void Text3DOverlay::update(float deltatime) {
- applyTransformTo(_transform);
+ if (usecTimestampNow() > _transformExpiry) {
+ Transform transform = getTransform();
+ applyTransformTo(transform);
+ setTransform(transform);
+ }
}
void Text3DOverlay::render(RenderArgs* args) {
if (!_visible || !getParentVisible()) {
return; // do nothing if we're not visible
}
-
+
Q_ASSERT(args->_batch);
auto& batch = *args->_batch;
-
- applyTransformTo(_transform, true);
- batch.setModelTransform(_transform);
+
+ Transform transform = getTransform();
+ applyTransformTo(transform, true);
+ setTransform(transform);
+ batch.setModelTransform(transform);
const float MAX_COLOR = 255.0f;
xColor backgroundColor = getBackgroundColor();
glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR,
backgroundColor.blue / MAX_COLOR, getBackgroundAlpha());
-
+
glm::vec2 dimensions = getDimensions();
glm::vec2 halfDimensions = dimensions * 0.5f;
-
+
const float SLIGHTLY_BEHIND = -0.001f;
-
+
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor);
-
+
// Same font properties as textSize()
float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
-
+
float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
-
+
glm::vec2 clipMinimum(0.0f, 0.0f);
glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor,
(dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor);
- Transform transform = _transform;
transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin),
halfDimensions.y - _topMargin, 0.001f));
transform.setScale(scaleFactor);
@@ -222,6 +227,8 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance,
BoxFace &face, glm::vec3& surfaceNormal) {
- applyTransformTo(_transform, true);
+ Transform transform = getTransform();
+ applyTransformTo(transform, true);
+ setTransform(transform);
return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal);
}
diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp
index 563198c976..ad61e28bc7 100644
--- a/interface/src/ui/overlays/Volume3DOverlay.cpp
+++ b/interface/src/ui/overlays/Volume3DOverlay.cpp
@@ -56,7 +56,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
// extents is the entity relative, scaled, centered extents of the entity
glm::mat4 worldToEntityMatrix;
- _transform.getInverseMatrix(worldToEntityMatrix);
+ getTransform().getInverseMatrix(worldToEntityMatrix);
glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));
diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp
index 1c84e71fa7..6ca3e49569 100644
--- a/interface/src/ui/overlays/Web3DOverlay.cpp
+++ b/interface/src/ui/overlays/Web3DOverlay.cpp
@@ -47,7 +47,7 @@ Web3DOverlay::~Web3DOverlay() {
_webSurface->disconnect(_connection);
// The lifetime of the QML surface MUST be managed by the main thread
// Additionally, we MUST use local variables copied by value, rather than
- // member variables, since they would implicitly refer to a this that
+ // member variables, since they would implicitly refer to a this that
// is no longer valid
auto webSurface = _webSurface;
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
@@ -57,7 +57,11 @@ Web3DOverlay::~Web3DOverlay() {
}
void Web3DOverlay::update(float deltatime) {
- applyTransformTo(_transform);
+ if (usecTimestampNow() > _transformExpiry) {
+ Transform transform = getTransform();
+ applyTransformTo(transform);
+ setTransform(transform);
+ }
}
void Web3DOverlay::render(RenderArgs* args) {
@@ -85,8 +89,9 @@ void Web3DOverlay::render(RenderArgs* args) {
vec2 halfSize = size / 2.0f;
vec4 color(toGlm(getColor()), getAlpha());
- applyTransformTo(_transform, true);
- Transform transform = _transform;
+ Transform transform = getTransform();
+ applyTransformTo(transform, true);
+ setTransform(transform);
if (glm::length2(getDimensions()) != 1.0f) {
transform.postScale(vec3(getDimensions(), 1.0f));
}
@@ -165,7 +170,10 @@ bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3&
// FIXME - face and surfaceNormal not being returned
// Make sure position and rotation is updated.
- applyTransformTo(_transform, true);
+ Transform transform;
+ applyTransformTo(transform, true);
+ setTransform(transform);
+
vec2 size = _resolution / _dpi * INCHES_TO_METERS * vec2(getDimensions());
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), size, distance);
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index dee0d1cb20..d0c7b3912c 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -1638,7 +1638,9 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
for (const auto& attachmentVar : variant) {
AttachmentData attachment;
attachment.fromVariant(attachmentVar);
- newAttachments.append(attachment);
+ if (!attachment.modelURL.isEmpty()) {
+ newAttachments.append(attachment);
+ }
}
setAttachmentData(newAttachments);
}
diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt
index fe08647074..315e7510a5 100644
--- a/libraries/display-plugins/CMakeLists.txt
+++ b/libraries/display-plugins/CMakeLists.txt
@@ -1,6 +1,7 @@
set(TARGET_NAME display-plugins)
+AUTOSCRIBE_SHADER_LIB(gpu display-plugins)
setup_hifi_library(OpenGL)
-link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui)
+link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui render-utils)
target_opengl()
diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
index f488a805c6..588c43d534 100644
--- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
@@ -33,18 +33,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
return Parent::internalActivate();
}
-void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
- _wantVsync = true; // always
- Parent::submitSceneTexture(frameIndex, sceneTexture);
-}
-
-void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
- if (_wantVsync != isVsyncEnabled()) {
- enableVsync(_wantVsync);
- }
- Parent::internalPresent();
-}
-
static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60;
bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h
index 6375425243..2e4e57e15a 100644
--- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h
@@ -24,10 +24,6 @@ public:
virtual bool internalActivate() override;
- virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
-
- virtual void internalPresent() override;
-
virtual bool isThrottled() const override;
protected:
@@ -40,5 +36,4 @@ private:
QAction* _vsyncAction { nullptr };
uint32_t _framerateTarget { 0 };
int _fullscreenTarget{ -1 };
- bool _wantVsync { true };
};
diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp
index 08368597d0..3e090dc7b3 100644
--- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp
@@ -12,6 +12,7 @@
#include "NullDisplayPlugin.h"
#include "stereo/SideBySideStereoDisplayPlugin.h"
#include "stereo/InterleavedStereoDisplayPlugin.h"
+#include "hmd/DebugHmdDisplayPlugin.h"
#include "Basic2DWindowOpenGLDisplayPlugin.h"
const QString& DisplayPlugin::MENU_PATH() {
@@ -23,6 +24,7 @@ const QString& DisplayPlugin::MENU_PATH() {
DisplayPluginList getDisplayPlugins() {
DisplayPlugin* PLUGIN_POOL[] = {
new Basic2DWindowOpenGLDisplayPlugin(),
+ new DebugHmdDisplayPlugin(),
#ifdef DEBUG
new NullDisplayPlugin(),
#endif
diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp
index 4fadbdb94b..5ee05fa2e3 100644
--- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp
@@ -11,6 +11,9 @@
#include
#include
+#include
+#include
+#include
const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
@@ -22,12 +25,10 @@ bool NullDisplayPlugin::hasFocus() const {
return false;
}
-void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
- _container->releaseSceneTexture(sceneTexture);
-}
-
-void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
- _container->releaseOverlayTexture(overlayTexture);
+void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) {
+ if (frame) {
+ _gpuContext->consumeFrameUpdates(frame);
+ }
}
QImage NullDisplayPlugin::getScreenshot() const {
diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h
index dfa4232a86..198c89ae78 100644
--- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h
@@ -11,16 +11,14 @@
class NullDisplayPlugin : public DisplayPlugin {
public:
+ ~NullDisplayPlugin() final {}
+ const QString& getName() const override { return NAME; }
+ grouping getGrouping() const override { return DEVELOPER; }
- virtual ~NullDisplayPlugin() final {}
- virtual const QString& getName() const override { return NAME; }
- virtual grouping getGrouping() const override { return DEVELOPER; }
-
- virtual glm::uvec2 getRecommendedRenderSize() const override;
- virtual bool hasFocus() const override;
- virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
- virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
- virtual QImage getScreenshot() const override;
+ glm::uvec2 getRecommendedRenderSize() const override;
+ bool hasFocus() const override;
+ void submitFrame(const gpu::FramePointer& newFrame) override;
+ QImage getScreenshot() const override;
private:
static const QString NAME;
};
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index e0c87fbbed..7a57e1d0f2 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -8,6 +8,7 @@
#include "OpenGLDisplayPlugin.h"
#include
+#include
#include
#include
@@ -19,26 +20,56 @@
#if defined(Q_OS_MAC)
#include
#endif
-#include
-#include
-#include
+
#include
#include
-#include
-#include
+#include
+
+#include
+#include
#include
#include
-#include
-#include
-#include "CompositorHelper.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
#include
+#include
+#include "CompositorHelper.h"
-#if THREADED_PRESENT
+const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
-// FIXME, for display plugins that don't block on something like vsync, just
-// cap the present rate at 200
-// const static unsigned int MAX_PRESENT_RATE = 200;
+uniform sampler2D colorMap;
+
+in vec2 varTexCoord0;
+
+out vec4 outFragColor;
+
+float sRGBFloatToLinear(float value) {
+ const float SRGB_ELBOW = 0.04045;
+
+ return (value <= SRGB_ELBOW) ? value / 12.92 : pow((value + 0.055) / 1.055, 2.4);
+}
+
+vec3 colorToLinearRGB(vec3 srgb) {
+ return vec3(sRGBFloatToLinear(srgb.r), sRGBFloatToLinear(srgb.g), sRGBFloatToLinear(srgb.b));
+}
+
+void main(void) {
+ outFragColor.a = 1.0;
+ outFragColor.rgb = colorToLinearRGB(texture(colorMap, varTexCoord0).rgb);
+}
+
+)SCRIBE";
+
+extern QThread* RENDER_THREAD;
class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex;
@@ -86,13 +117,22 @@ public:
virtual void run() override {
+ // FIXME determine the best priority balance between this and the main thread...
+ // It may be dependent on the display plugin being used, since VR plugins should
+ // have higher priority on rendering (although we could say that the Oculus plugin
+ // doesn't need that since it has async timewarp).
+ // A higher priority here
+ setPriority(QThread::HighPriority);
OpenGLDisplayPlugin* currentPlugin{ nullptr };
- thread()->setPriority(QThread::HighestPriority);
Q_ASSERT(_context);
+ _context->makeCurrent();
+ Q_ASSERT(isCurrentContext(_context->contextHandle()));
while (!_shutdown) {
if (_pendingMainThreadOperation) {
+ PROFILE_RANGE("MainThreadOp")
{
Lock lock(_mutex);
+ _context->doneCurrent();
// Move the context to the main thread
_context->moveToThread(qApp->thread());
_pendingMainThreadOperation = false;
@@ -111,33 +151,46 @@ public:
// Check for a new display plugin
{
Lock lock(_mutex);
- _context->makeCurrent();
// Check if we have a new plugin to activate
while (!_newPluginQueue.empty()) {
auto newPlugin = _newPluginQueue.front();
if (newPlugin != currentPlugin) {
// Deactivate the old plugin
if (currentPlugin != nullptr) {
- try {
- currentPlugin->uncustomizeContext();
- } catch (const oglplus::Error& error) {
- qWarning() << "OpenGL error in uncustomizeContext: " << error.what();
- }
+ _context->makeCurrent();
+ currentPlugin->uncustomizeContext();
+ CHECK_GL_ERROR();
+ _context->doneCurrent();
}
if (newPlugin) {
- try {
- newPlugin->customizeContext();
- } catch (const oglplus::Error& error) {
- qWarning() << "OpenGL error in customizeContext: " << error.what();
- }
+ bool hasVsync = true;
+ bool wantVsync = newPlugin->wantVsync();
+ _context->makeCurrent();
+#if defined(Q_OS_WIN)
+ wglSwapIntervalEXT(wantVsync ? 1 : 0);
+ hasVsync = wglGetSwapIntervalEXT() != 0;
+#elif defined(Q_OS_MAC)
+ GLint interval = wantVsync ? 1 : 0;
+ newPlugin->swapBuffers();
+ CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
+ newPlugin->swapBuffers();
+ CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
+ hasVsync = interval != 0;
+#else
+ // TODO: Fill in for linux
+ Q_UNUSED(wantVsync);
+#endif
+ newPlugin->setVsyncEnabled(hasVsync);
+ newPlugin->customizeContext();
+ CHECK_GL_ERROR();
+ _context->doneCurrent();
}
currentPlugin = newPlugin;
_newPluginQueue.pop();
_condition.notify_one();
}
}
- _context->doneCurrent();
}
// If there's no active plugin, just sleep
@@ -147,18 +200,14 @@ public:
continue;
}
- // take the latest texture and present it
+ // Execute the frame and present it to the display device.
_context->makeCurrent();
- if (isCurrentContext(_context->contextHandle())) {
- try {
- currentPlugin->present();
- } catch (const oglplus::Error& error) {
- qWarning() << "OpenGL error in presentation: " << error.what();
- }
- _context->doneCurrent();
- } else {
- qWarning() << "Makecurrent failed";
+ {
+ PROFILE_RANGE("PluginPresent")
+ currentPlugin->present();
+ CHECK_GL_ERROR();
}
+ _context->doneCurrent();
}
Lock lock(_mutex);
@@ -204,27 +253,6 @@ private:
QGLContext* _context { nullptr };
};
-#endif
-
-
-OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
- _sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){
- cleanupForSceneTexture(texture);
- _container->releaseSceneTexture(texture);
- });
- _overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) {
- _container->releaseOverlayTexture(texture);
- });
-}
-
-void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) {
- withRenderThreadLock([&] {
- Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture));
- _sceneTextureToFrameIndexMap.remove(sceneTexture);
- });
-}
-
-
bool OpenGLDisplayPlugin::activate() {
if (!_cursorsData.size()) {
auto& cursorManager = Cursor::Manager::instance();
@@ -242,9 +270,7 @@ bool OpenGLDisplayPlugin::activate() {
if (!_container) {
return false;
}
- _vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
-#if THREADED_PRESENT
// Start the present thread if necessary
QSharedPointer presentThread;
if (DependencyManager::isSet()) {
@@ -259,7 +285,9 @@ bool OpenGLDisplayPlugin::activate() {
presentThread->start();
}
_presentThread = presentThread.data();
-#endif
+ if (!RENDER_THREAD) {
+ RENDER_THREAD = _presentThread;
+ }
// Child classes may override this in order to do things like initialize
// libraries, etc
@@ -267,17 +295,10 @@ bool OpenGLDisplayPlugin::activate() {
return false;
}
-#if THREADED_PRESENT
// This should not return until the new context has been customized
// and the old context (if any) has been uncustomized
presentThread->setNewDisplayPlugin(this);
-#else
- static auto widget = _container->getPrimaryWidget();
- widget->makeCurrent();
- customizeContext();
- _container->makeRenderingContextCurrent();
-#endif
auto compositorHelper = DependencyManager::get();
connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] {
@@ -296,20 +317,12 @@ bool OpenGLDisplayPlugin::activate() {
}
void OpenGLDisplayPlugin::deactivate() {
-
auto compositorHelper = DependencyManager::get();
disconnect(compositorHelper.data());
-#if THREADED_PRESENT
auto presentThread = DependencyManager::get();
// Does not return until the GL transition has completeed
presentThread->setNewDisplayPlugin(nullptr);
-#else
- static auto widget = _container->getPrimaryWidget();
- widget->makeCurrent();
- uncustomizeContext();
- _container->makeRenderingContextCurrent();
-#endif
internalDeactivate();
_container->showDisplayPluginsTools(false);
@@ -323,58 +336,99 @@ void OpenGLDisplayPlugin::deactivate() {
Parent::deactivate();
}
-
void OpenGLDisplayPlugin::customizeContext() {
-#if THREADED_PRESENT
auto presentThread = DependencyManager::get();
Q_ASSERT(thread() == presentThread->thread());
-#endif
- enableVsync();
+
+ getGLBackend()->setCameraCorrection(mat4());
for (auto& cursorValue : _cursorsData) {
auto& cursorData = cursorValue.second;
if (!cursorData.texture) {
- const auto& image = cursorData.image;
- glGenTextures(1, &cursorData.texture);
- glBindTexture(GL_TEXTURE_2D, cursorData.texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glGenerateMipmap(GL_TEXTURE_2D);
+ auto image = cursorData.image;
+ if (image.format() != QImage::Format_ARGB32) {
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ }
+ if ((image.width() > 0) && (image.height() > 0)) {
+
+ cursorData.texture.reset(
+ gpu::Texture::create2D(
+ gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
+ image.width(), image.height(),
+ gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
+ auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
+ cursorData.texture->setUsage(usage.build());
+ cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
+ cursorData.texture->autoGenerateMips(-1);
+ }
}
- glBindTexture(GL_TEXTURE_2D, 0);
}
- using namespace oglplus;
- Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
- Context::Disable(Capability::Blend);
- Context::Disable(Capability::DepthTest);
- Context::Disable(Capability::CullFace);
-
- _program = loadDefaultShader();
-
- auto uniforms = _program->ActiveUniforms();
- while (!uniforms.Empty()) {
- auto uniform = uniforms.Front();
- if (uniform.Name() == "mvp") {
- _mvpUniform = uniform.Index();
+ if (!_presentPipeline) {
+ {
+ auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
+ auto ps = gpu::StandardShaderLib::getDrawTexturePS();
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+ gpu::Shader::makeProgram(*program);
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false));
+ state->setScissorEnable(true);
+ _simplePipeline = gpu::Pipeline::create(program, state);
}
- if (uniform.Name() == "alpha") {
- _alphaUniform = uniform.Index();
+
+ {
+ auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
+ auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG));
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+ gpu::Shader::makeProgram(*program);
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false));
+ state->setScissorEnable(true);
+ _presentPipeline = gpu::Pipeline::create(program, state);
+ }
+
+ {
+ auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
+ auto ps = gpu::StandardShaderLib::getDrawTexturePS();
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+ gpu::Shader::makeProgram(*program);
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false));
+ state->setBlendFunction(true,
+ gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+ gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+ _overlayPipeline = gpu::Pipeline::create(program, state);
+ }
+
+ {
+ auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
+ auto ps = gpu::StandardShaderLib::getDrawTexturePS();
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+ gpu::Shader::makeProgram(*program);
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false));
+ state->setBlendFunction(true,
+ gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+ gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+ _cursorPipeline = gpu::Pipeline::create(program, state);
}
- uniforms.Next();
}
-
- _plane = loadPlane(_program);
-
- _compositeFramebuffer = std::make_shared();
- _compositeFramebuffer->Init(getRecommendedRenderSize());
+ auto renderSize = getRecommendedRenderSize();
+ _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
}
void OpenGLDisplayPlugin::uncustomizeContext() {
+ _presentPipeline.reset();
+ _cursorPipeline.reset();
+ _overlayPipeline.reset();
_compositeFramebuffer.reset();
- _program.reset();
- _plane.reset();
+ withPresentThreadLock([&] {
+ _currentFrame.reset();
+ while (!_newFrameQueue.empty()) {
+ _gpuContext->consumeFrameUpdates(_newFrameQueue.front());
+ _newFrameQueue.pop();
+ }
+ });
}
@@ -420,172 +474,161 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
return false;
}
-void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
+void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
if (_lockCurrentTexture) {
- _container->releaseSceneTexture(sceneTexture);
return;
}
- withRenderThreadLock([&] {
- _sceneTextureToFrameIndexMap[sceneTexture] = frameIndex;
+ withNonPresentThreadLock([&] {
+ _newFrameQueue.push(newFrame);
});
-
- // Submit it to the presentation thread via escrow
- _sceneTextureEscrow.submit(sceneTexture);
-
-#if THREADED_PRESENT
-#else
- static auto widget = _container->getPrimaryWidget();
- widget->makeCurrent();
- present();
- _container->makeRenderingContextCurrent();
-#endif
-}
-
-void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
- // Submit it to the presentation thread via escrow
- _overlayTextureEscrow.submit(overlayTexture);
-}
-
-void OpenGLDisplayPlugin::updateTextures() {
- // FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
-#if THREADED_PRESENT
- if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
-#else
- if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
-#endif
- updateFrameData();
- _newFrameRate.increment();
- }
-
- _overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture);
}
void OpenGLDisplayPlugin::updateFrameData() {
withPresentThreadLock([&] {
- auto previousFrameIndex = _currentPresentFrameIndex;
- _currentPresentFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
- auto skippedCount = (_currentPresentFrameIndex - previousFrameIndex) - 1;
+ gpu::FramePointer oldFrame = _currentFrame;
+ uint32_t skippedCount = 0;
+ if (!_newFrameQueue.empty()) {
+ // We're changing frames, so we can cleanup any GL resources that might have been used by the old frame
+ _gpuContext->recycle();
+ }
+ while (!_newFrameQueue.empty()) {
+ _currentFrame = _newFrameQueue.front();
+ _newFrameQueue.pop();
+ _gpuContext->consumeFrameUpdates(_currentFrame);
+ if (_currentFrame && oldFrame) {
+ skippedCount += (_currentFrame->frameIndex - oldFrame->frameIndex) - 1;
+ }
+ }
_droppedFrameRate.increment(skippedCount);
});
}
void OpenGLDisplayPlugin::compositeOverlay() {
- using namespace oglplus;
-
- auto compositorHelper = DependencyManager::get();
-
- useProgram(_program);
- // set the alpha
- Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
- // check the alpha
- // Overlay draw
- if (isStereo()) {
- Uniform(*_program, _mvpUniform).Set(mat4());
- for_each_eye([&](Eye eye) {
- eyeViewport(eye);
- drawUnitQuad();
- });
- } else {
- // Overlay draw
- Uniform(*_program, _mvpUniform).Set(mat4());
- drawUnitQuad();
- }
- // restore the alpha
- Uniform(*_program, _alphaUniform).Set(1.0);
-}
-
-void OpenGLDisplayPlugin::compositePointer() {
- using namespace oglplus;
- auto compositorHelper = DependencyManager::get();
-
- useProgram(_program);
- // set the alpha
- Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
- Uniform(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
- if (isStereo()) {
- for_each_eye([&](Eye eye) {
- eyeViewport(eye);
- drawUnitQuad();
- });
- } else {
- drawUnitQuad();
- }
- Uniform(*_program, _mvpUniform).Set(mat4());
- // restore the alpha
- Uniform(*_program, _alphaUniform).Set(1.0);
-}
-
-void OpenGLDisplayPlugin::compositeScene() {
- using namespace oglplus;
- useProgram(_program);
- Uniform(*_program, _mvpUniform).Set(mat4());
- drawUnitQuad();
-}
-
-void OpenGLDisplayPlugin::compositeLayers() {
- using namespace oglplus;
- auto targetRenderSize = getRecommendedRenderSize();
- if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) {
- _compositeFramebuffer = std::make_shared();
- _compositeFramebuffer->Init(targetRenderSize);
- }
- _compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] {
- Context::Viewport(targetRenderSize.x, targetRenderSize.y);
- auto sceneTextureId = getSceneTextureId();
- auto overlayTextureId = getOverlayTextureId();
- glBindTexture(GL_TEXTURE_2D, sceneTextureId);
- compositeScene();
- if (overlayTextureId) {
- glBindTexture(GL_TEXTURE_2D, overlayTextureId);
- Context::Enable(Capability::Blend);
- Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
- compositeOverlay();
-
- auto compositorHelper = DependencyManager::get();
- if (compositorHelper->getReticleVisible()) {
- auto& cursorManager = Cursor::Manager::instance();
- const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
- glBindTexture(GL_TEXTURE_2D, cursorData.texture);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, overlayTextureId);
- compositePointer();
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
- }
- glBindTexture(GL_TEXTURE_2D, 0);
- Context::Disable(Capability::Blend);
+ render([&](gpu::Batch& batch){
+ batch.enableStereo(false);
+ batch.setFramebuffer(_compositeFramebuffer);
+ batch.setPipeline(_overlayPipeline);
+ batch.setResourceTexture(0, _currentFrame->overlay);
+ if (isStereo()) {
+ for_each_eye([&](Eye eye) {
+ batch.setViewportTransform(eyeViewport(eye));
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ });
+ } else {
+ batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
}
- compositeExtra();
});
}
+void OpenGLDisplayPlugin::compositePointer() {
+ auto& cursorManager = Cursor::Manager::instance();
+ const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
+ auto cursorTransform = DependencyManager::get()->getReticleTransform(glm::mat4());
+ render([&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+ batch.setProjectionTransform(mat4());
+ batch.setFramebuffer(_compositeFramebuffer);
+ batch.setPipeline(_cursorPipeline);
+ batch.setResourceTexture(0, cursorData.texture);
+ batch.resetViewTransform();
+ batch.setModelTransform(cursorTransform);
+ if (isStereo()) {
+ for_each_eye([&](Eye eye) {
+ batch.setViewportTransform(eyeViewport(eye));
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ });
+ } else {
+ batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ }
+ });
+}
+
+void OpenGLDisplayPlugin::compositeScene() {
+ render([&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+ batch.setFramebuffer(_compositeFramebuffer);
+ batch.setViewportTransform(ivec4(uvec2(), _compositeFramebuffer->getSize()));
+ batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize()));
+ batch.resetViewTransform();
+ batch.setProjectionTransform(mat4());
+ batch.setPipeline(_simplePipeline);
+ batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ });
+}
+
+void OpenGLDisplayPlugin::compositeLayers() {
+ auto renderSize = getRecommendedRenderSize();
+ if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
+ _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
+ }
+
+ {
+ PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount())
+ compositeScene();
+ }
+
+ {
+ PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount())
+ compositeOverlay();
+ }
+ auto compositorHelper = DependencyManager::get();
+ if (compositorHelper->getReticleVisible()) {
+ PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount())
+ compositePointer();
+ }
+
+ {
+ PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount())
+ compositeExtra();
+ }
+}
+
void OpenGLDisplayPlugin::internalPresent() {
- using namespace oglplus;
- const uvec2& srcSize = _compositeFramebuffer->size;
- uvec2 dstSize = getSurfacePixels();
- _compositeFramebuffer->Bound(FramebufferTarget::Read, [&] {
- Context::BlitFramebuffer(
- 0, 0, srcSize.x, srcSize.y,
- 0, 0, dstSize.x, dstSize.y,
- BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
+ render([&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+ batch.resetViewTransform();
+ batch.setFramebuffer(gpu::FramebufferPointer());
+ batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
+ batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
+ batch.setPipeline(_presentPipeline);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
});
swapBuffers();
}
void OpenGLDisplayPlugin::present() {
+ PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount())
+ updateFrameData();
incrementPresentCount();
- PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
+ {
+ PROFILE_RANGE_EX("recycle", 0xff00ff00, (uint64_t)presentCount())
+ _gpuContext->recycle();
+ }
+
+ if (_currentFrame) {
+ {
+ // Execute the frame rendering commands
+ PROFILE_RANGE_EX("execute", 0xff00ff00, (uint64_t)presentCount())
+ _gpuContext->executeFrame(_currentFrame);
+ }
- updateTextures();
- if (_currentSceneTexture) {
// Write all layers to a local framebuffer
- compositeLayers();
+ {
+ PROFILE_RANGE_EX("composite", 0xff00ffff, (uint64_t)presentCount())
+ compositeLayers();
+ }
+
// Take the composite framebuffer and send it to the output device
- internalPresent();
+ {
+ PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount())
+ internalPresent();
+ }
_presentRate.increment();
- _activeProgram.reset();
}
}
@@ -595,7 +638,7 @@ float OpenGLDisplayPlugin::newFramePresentRate() const {
float OpenGLDisplayPlugin::droppedFrameRate() const {
float result;
- withRenderThreadLock([&] {
+ withNonPresentThreadLock([&] {
result = _droppedFrameRate.rate();
});
return result;
@@ -605,97 +648,27 @@ float OpenGLDisplayPlugin::presentRate() const {
return _presentRate.rate();
}
-void OpenGLDisplayPlugin::drawUnitQuad() {
- useProgram(_program);
- _plane->Use();
- _plane->Draw();
-}
-
-void OpenGLDisplayPlugin::enableVsync(bool enable) {
- if (!_vsyncSupported) {
- return;
- }
-#if defined(Q_OS_WIN)
- wglSwapIntervalEXT(enable ? 1 : 0);
-#elif defined(Q_OS_MAC)
- GLint interval = enable ? 1 : 0;
- CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
-#else
- // TODO: Fill in for linux
- return;
-#endif
-}
-
-bool OpenGLDisplayPlugin::isVsyncEnabled() {
- if (!_vsyncSupported) {
- return true;
- }
-#if defined(Q_OS_WIN)
- return wglGetSwapIntervalEXT() != 0;
-#elif defined(Q_OS_MAC)
- GLint interval;
- CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
- return interval != 0;
-#else
- // TODO: Fill in for linux
- return true;
-#endif
-}
-
void OpenGLDisplayPlugin::swapBuffers() {
static auto widget = _container->getPrimaryWidget();
widget->swapBuffers();
}
void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const {
-#if THREADED_PRESENT
static auto presentThread = DependencyManager::get();
presentThread->withMainThreadContext(f);
_container->makeRenderingContextCurrent();
-#else
- static auto widget = _container->getPrimaryWidget();
- widget->makeCurrent();
- f();
- _container->makeRenderingContextCurrent();
-#endif
}
QImage OpenGLDisplayPlugin::getScreenshot() const {
- using namespace oglplus;
- QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888);
+ auto size = _compositeFramebuffer->getSize();
+ auto glBackend = const_cast(*this).getGLBackend();
+ QImage screenshot(size.x, size.y, QImage::Format_ARGB32);
withMainThreadContext([&] {
- Framebuffer::Bind(Framebuffer::Target::Read, _compositeFramebuffer->fbo);
- Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits());
+ glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(uvec2(0), size), screenshot);
});
return screenshot.mirrored(false, true);
}
-uint32_t OpenGLDisplayPlugin::getSceneTextureId() const {
- if (!_currentSceneTexture) {
- return 0;
- }
-
- return _currentSceneTexture->getHardwareId();
-}
-
-uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const {
- if (!_currentOverlayTexture) {
- return 0;
- }
- return _currentOverlayTexture->getHardwareId();
-}
-
-void OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
- using namespace oglplus;
- uvec2 vpSize = _compositeFramebuffer->size;
- vpSize.x /= 2;
- uvec2 vpPos;
- if (eye == Eye::Right) {
- vpPos.x = vpSize.x;
- }
- Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y);
-}
-
glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const {
uvec2 result;
auto window = _container->getPrimaryWidget();
@@ -719,14 +692,7 @@ bool OpenGLDisplayPlugin::hasFocus() const {
return window ? window->hasFocus() : false;
}
-void OpenGLDisplayPlugin::useProgram(const ProgramPtr& program) {
- if (_activeProgram != program) {
- program->Bind();
- _activeProgram = program;
- }
-}
-
-void OpenGLDisplayPlugin::assertIsRenderThread() const {
+void OpenGLDisplayPlugin::assertNotPresentThread() const {
Q_ASSERT(QThread::currentThread() != _presentThread);
}
@@ -735,8 +701,39 @@ void OpenGLDisplayPlugin::assertIsPresentThread() const {
}
bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
- withRenderThreadLock([&] {
+ withNonPresentThreadLock([&] {
_compositeOverlayAlpha = _overlayAlpha;
});
return Parent::beginFrameRender(frameIndex);
}
+
+ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
+ uvec2 vpSize = _currentFrame->framebuffer->getSize();
+ vpSize.x /= 2;
+ uvec2 vpPos;
+ if (eye == Eye::Right) {
+ vpPos.x = vpSize.x;
+ }
+ return ivec4(vpPos, vpSize);
+}
+
+gpu::gl::GLBackend* OpenGLDisplayPlugin::getGLBackend() {
+ if (!_gpuContext || !_gpuContext->getBackend()) {
+ return nullptr;
+ }
+ auto backend = _gpuContext->getBackend().get();
+#if defined(Q_OS_MAC)
+ // Should be dynamic_cast, but that doesn't work in plugins on OSX
+ auto glbackend = static_cast(backend);
+#else
+ auto glbackend = dynamic_cast(backend);
+#endif
+
+ return glbackend;
+}
+
+void OpenGLDisplayPlugin::render(std::function f) {
+ gpu::Batch batch;
+ f(batch);
+ _gpuContext->executeBatch(batch);
+}
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
index 068b236289..48f9a78eda 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
@@ -11,17 +11,21 @@
#include
#include
+#include
#include
#include
#include
#include
-#include
#include
#include
-#define THREADED_PRESENT 1
+namespace gpu {
+ namespace gl {
+ class GLBackend;
+ }
+}
class OpenGLDisplayPlugin : public DisplayPlugin {
Q_OBJECT
@@ -33,19 +37,14 @@ protected:
using Condition = std::condition_variable;
using TextureEscrow = GLEscrow;
public:
- OpenGLDisplayPlugin();
-
// These must be final to ensure proper ordering of operations
// between the main thread and the presentation thread
bool activate() override final;
void deactivate() override final;
-
bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; }
-
- void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
- void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
+ void submitFrame(const gpu::FramePointer& newFrame) override;
glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels();
@@ -64,17 +63,18 @@ public:
float droppedFrameRate() const override;
bool beginFrameRender(uint32_t frameIndex) override;
+
+ virtual bool wantVsync() const { return true; }
+ void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; }
+ bool isVsyncEnabled() const { return _vsyncEnabled; }
+
protected:
-#if THREADED_PRESENT
friend class PresentThread;
-#endif
- uint32_t getSceneTextureId() const;
- uint32_t getOverlayTextureId() const;
glm::uvec2 getSurfaceSize() const;
glm::uvec2 getSurfacePixels() const;
- void compositeLayers();
+ virtual void compositeLayers();
virtual void compositeScene();
virtual void compositeOverlay();
virtual void compositePointer();
@@ -82,10 +82,6 @@ protected:
virtual bool hasFocus() const override;
- // FIXME make thread safe?
- virtual bool isVsyncEnabled();
- virtual void enableVsync(bool enable = true);
-
// These functions must only be called on the presentation thread
virtual void customizeContext();
virtual void uncustomizeContext();
@@ -93,54 +89,46 @@ protected:
// Returns true on successful activation
virtual bool internalActivate() { return true; }
virtual void internalDeactivate() {}
- virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture);
+
// Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent();
- void withMainThreadContext(std::function f) const;
-
- void useProgram(const ProgramPtr& program);
- void present();
- void updateTextures();
- void drawUnitQuad();
- void swapBuffers();
- void eyeViewport(Eye eye) const;
-
virtual void updateFrameData();
- QThread* _presentThread{ nullptr };
- ProgramPtr _program;
- int32_t _mvpUniform { -1 };
- int32_t _alphaUniform { -1 };
- ShapeWrapperPtr _plane;
+ void withMainThreadContext(std::function f) const;
+ void present();
+ virtual void swapBuffers();
+ ivec4 eyeViewport(Eye eye) const;
+
+ void render(std::function f);
+
+ bool _vsyncEnabled { true };
+ QThread* _presentThread{ nullptr };
+ std::queue _newFrameQueue;
RateCounter<> _droppedFrameRate;
RateCounter<> _newFrameRate;
RateCounter<> _presentRate;
- QMap _sceneTextureToFrameIndexMap;
- uint32_t _currentPresentFrameIndex { 0 };
- float _compositeOverlayAlpha{ 1.0f };
- gpu::TexturePointer _currentSceneTexture;
- gpu::TexturePointer _currentOverlayTexture;
-
- TextureEscrow _sceneTextureEscrow;
- TextureEscrow _overlayTextureEscrow;
-
- bool _vsyncSupported { false };
+ gpu::FramePointer _currentFrame;
+ gpu::FramebufferPointer _compositeFramebuffer;
+ gpu::PipelinePointer _overlayPipeline;
+ gpu::PipelinePointer _simplePipeline;
+ gpu::PipelinePointer _presentPipeline;
+ gpu::PipelinePointer _cursorPipeline;
+ float _compositeOverlayAlpha { 1.0f };
struct CursorData {
QImage image;
vec2 hotSpot;
uvec2 size;
- uint32_t texture { 0 };
+ gpu::TexturePointer texture;
};
std::map _cursorsData;
- BasicFramebufferWrapperPtr _compositeFramebuffer;
bool _lockCurrentTexture { false };
- void assertIsRenderThread() const;
+ void assertNotPresentThread() const;
void assertIsPresentThread() const;
template
@@ -151,17 +139,17 @@ protected:
}
template
- void withRenderThreadLock(F f) const {
- assertIsRenderThread();
+ void withNonPresentThreadLock(F f) const {
+ assertNotPresentThread();
Lock lock(_presentMutex);
f();
}
-private:
+ gpu::gl::GLBackend* getGLBackend();
+
// Any resource shared by the main thread and the presentation thread must
// be serialized through this mutex
mutable Mutex _presentMutex;
- ProgramPtr _activeProgram;
float _overlayAlpha{ 1.0f };
};
diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp
new file mode 100644
index 0000000000..fa267e2c68
--- /dev/null
+++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp
@@ -0,0 +1,90 @@
+//
+// Created by Bradley Austin Davis on 2016/07/31
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#include "DebugHmdDisplayPlugin.h"
+
+#include
+
+#include
+#include
+#include
+
+const QString DebugHmdDisplayPlugin::NAME("HMD Simulator");
+
+static const QString DEBUG_FLAG("HIFI_DEBUG_HMD");
+static bool enableDebugHmd = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
+
+
+bool DebugHmdDisplayPlugin::isSupported() const {
+ return enableDebugHmd;
+}
+
+void DebugHmdDisplayPlugin::resetSensors() {
+ _currentRenderFrameInfo.renderPose = glm::mat4(); // identity
+}
+
+bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
+ _currentRenderFrameInfo = FrameInfo();
+ _currentRenderFrameInfo.sensorSampleTime = secTimestampNow();
+ _currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime;
+ // FIXME simulate head movement
+ //_currentRenderFrameInfo.renderPose = ;
+ //_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
+
+ withNonPresentThreadLock([&] {
+ _uiModelTransform = DependencyManager::get()->getModelTransform();
+ _frameInfos[frameIndex] = _currentRenderFrameInfo;
+
+ _handPoses[0] = glm::translate(mat4(), vec3(-0.3f, 0.0f, 0.0f));
+ _handLasers[0].color = vec4(1, 0, 0, 1);
+ _handLasers[0].mode = HandLaserMode::Overlay;
+
+ _handPoses[1] = glm::translate(mat4(), vec3(0.3f, 0.0f, 0.0f));
+ _handLasers[1].color = vec4(0, 1, 1, 1);
+ _handLasers[1].mode = HandLaserMode::Overlay;
+ });
+ return Parent::beginFrameRender(frameIndex);
+}
+
+// DLL based display plugins MUST initialize GLEW inside the DLL code.
+void DebugHmdDisplayPlugin::customizeContext() {
+ glewExperimental = true;
+ glewInit();
+ glGetError(); // clear the potential error from glewExperimental
+ Parent::customizeContext();
+}
+
+bool DebugHmdDisplayPlugin::internalActivate() {
+ _ipd = 0.0327499993f * 2.0f;
+ _eyeProjections[0][0] = vec4{ 0.759056330, 0.000000000, 0.000000000, 0.000000000 };
+ _eyeProjections[0][1] = vec4{ 0.000000000, 0.682773232, 0.000000000, 0.000000000 };
+ _eyeProjections[0][2] = vec4{ -0.0580431037, -0.00619550655, -1.00000489, -1.00000000 };
+ _eyeProjections[0][3] = vec4{ 0.000000000, 0.000000000, -0.0800003856, 0.000000000 };
+ _eyeProjections[1][0] = vec4{ 0.752847493, 0.000000000, 0.000000000, 0.000000000 };
+ _eyeProjections[1][1] = vec4{ 0.000000000, 0.678060353, 0.000000000, 0.000000000 };
+ _eyeProjections[1][2] = vec4{ 0.0578232110, -0.00669418881, -1.00000489, -1.000000000 };
+ _eyeProjections[1][3] = vec4{ 0.000000000, 0.000000000, -0.0800003856, 0.000000000 };
+ _eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]);
+ _eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]);
+ _eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, 0.0149999997, 1.0 };
+ _eyeOffsets[0][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 };
+ _renderTargetSize = { 3024, 1680 };
+ _cullingProjection = _eyeProjections[0];
+ // This must come after the initialization, so that the values calculated
+ // above are available during the customizeContext call (when not running
+ // in threaded present mode)
+ return Parent::internalActivate();
+}
+
+void DebugHmdDisplayPlugin::updatePresentPose() {
+ float yaw = sinf(secTimestampNow()) * 0.25f;
+ float pitch = cosf(secTimestampNow()) * 0.25f;
+ // Simulates head pose latency correction
+ _currentPresentFrameInfo.presentPose =
+ glm::mat4_cast(glm::angleAxis(yaw, Vectors::UP)) *
+ glm::mat4_cast(glm::angleAxis(pitch, Vectors::RIGHT));
+}
diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h
new file mode 100644
index 0000000000..509e13eda7
--- /dev/null
+++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h
@@ -0,0 +1,33 @@
+//
+// Created by Bradley Austin Davis on 2016/07/31
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#pragma once
+
+#include "HmdDisplayPlugin.h"
+
+class DebugHmdDisplayPlugin : public HmdDisplayPlugin {
+ using Parent = HmdDisplayPlugin;
+
+public:
+ const QString& getName() const override { return NAME; }
+ grouping getGrouping() const override { return DEVELOPER; }
+
+ bool isSupported() const override;
+ void resetSensors() override final;
+ bool beginFrameRender(uint32_t frameIndex) override;
+ float getTargetFrameRate() const override { return 90; }
+
+
+protected:
+ void updatePresentPose() override;
+ void hmdPresent() override {}
+ bool isHmdMounted() const override { return true; }
+ void customizeContext() override;
+ bool internalActivate() override;
+private:
+ static const QString NAME;
+};
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
index 306bc26a17..c916fcafe2 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
@@ -1,10 +1,11 @@
-//
+//
// Created by Bradley Austin Davis on 2016/02/15
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+
#include "HmdDisplayPlugin.h"
#include
@@ -22,9 +23,10 @@
#include
#include
#include
-
-#include
-#include
+#include
+#include
+#include
+#include