fixed merge conflicts in Application.cpp

This commit is contained in:
elisa-lj11 2016-08-19 09:46:22 -07:00
commit 4c434e875e
340 changed files with 10077 additions and 6359 deletions

View file

@ -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"}]
}
};

View file

@ -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;

View file

@ -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);
}

View file

@ -172,6 +172,7 @@ protected:
QString _persistFilePath;
QString _persistAsFileType;
QString _backupDirectoryPath;
int _packetsPerClientPerInterval;
int _packetsTotalPerInterval;
OctreePointer _tree; // this IS a reaveraging tree

View file

@ -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 ""

View file

@ -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()

View file

@ -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()

View file

@ -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.<br/>If this path is relative it will be relative to the application data directory.",
"placeholder": "",
"default": "",
"advanced": true
},
{
"name": "persistInterval",
"label": "Save Check Interval",

View file

@ -25,7 +25,7 @@
<ul class="nav nav-pills nav-stacked">
</ul>
<button id="advanced-toggle-button" hidden=true class="btn btn-info advanced-toggle">Show advanced</button>
<button id="advanced-toggle-button" class="btn btn-info advanced-toggle">Show advanced</button>
<button class="btn btn-success save-button">Save and restart</button>
</div>
</div>

View file

@ -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
}

View file

@ -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<LimitedNodeList>()->setSessionUUID(QUuid { id });
// store the new token to the account info
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->setTemporaryDomain(id, key);
// change our domain ID immediately
DependencyManager::get<LimitedNodeList>()->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<AccountManager>()->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<LimitedNodeList>();
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<LimitedNodeList>();
// 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";

View file

@ -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;
};

View file

@ -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<QUuid, QUuid>& groupsForName = _groupMembership[name];
const QHash<QUuid, QUuid>& 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<QUuid, QUuid>& groupsForUser = _groupMembership[userName];
QHash<QUuid, QUuid>& groupsForUser = _groupMembership[userName.toLower()];
QString line = "";
foreach (QUuid groupID, groupsForUser.keys()) {
line += " g=" + groupID.toString() + ",r=" + groupsForUser[groupID].toString();

View file

@ -84,7 +84,7 @@ public:
QList<QUuid> 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

View file

@ -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" },

View file

@ -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 @@
]
}

View file

@ -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" },

View file

@ -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" },

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -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

View file

@ -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
}
}

View file

@ -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: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
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")
}
}
}

View file

@ -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: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
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: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
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
}
}
}

View file

@ -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
}
}
}

View file

@ -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 <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
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
}
}
}

View file

@ -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 <b>") : qsTr("Welcome <b>")) + Account.username + qsTr("</b>!")
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()
}
}

View file

@ -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)
}
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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: {

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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;

View file

@ -87,15 +87,16 @@
#include <PhysicsHelpers.h>
#include <plugins/PluginManager.h>
#include <plugins/CodecPlugin.h>
#include <RecordingScriptingInterface.h>
#include <RenderableWebEntityItem.h>
#include <RenderShadowTask.h>
#include <RenderDeferredTask.h>
#include <ResourceCache.h>
#include <SceneScriptingInterface.h>
#include <RecordingScriptingInterface.h>
#include <ScriptEngines.h>
#include <ScriptCache.h>
#include <SoundCache.h>
#include <ScriptEngines.h>
#include <steamworks-wrapper/SteamClient.h>
#include <Tooltip.h>
#include <udt/PacketHeaders.h>
#include <UserActivityLogger.h>
@ -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<InterfaceActionFactory>();
DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<MessagesClient>();
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<UserInputMapper>();
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
DependencyManager::set<InterfaceParentFinder>();
@ -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<const char**>(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> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
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<TextureCache>();
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<NodeList>()->soloNodeOfType(NodeType::EntityServer);
return entityServerNode && !isPhysicsEnabled();
});
// After all of the constructor is completed, then set firstRun to false.
Setting::Handle<bool> 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<gpu::gl::GLBackend>();
_gpuContext = std::make_shared<gpu::Context>();
// 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>();
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<EntityScriptingInterface>().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<MessagesClient>().data());
rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().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<FramebufferCache>();
// 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<FramebufferCache>();
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<HMDScriptingInterface>();
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<FramebufferCache>()->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<bool> 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<BackgroundRenderData> 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<SceneScriptingInterface>()->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<BackgroundRenderData>() ... 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<SceneScriptingInterface>()->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<SceneScriptingInterface>()->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<UserActivityLoggerScriptingInterface>().data());
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().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<NodeList>()->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<ScriptEngines>()->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>();
framebufferCache->releaseFramebuffer(framebufferPointer);
}
void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) {
_applicationOverlay.releaseOverlay(texture);
}
bool Application::isForeground() const {
return _isForeground && !_window->isMinimized();
}

View file

@ -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 <procedural/ProceduralSkybox.h>
#include <model/Skybox.h>
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<gpu::TexturePointer, gpu::FramebufferPointer> _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

View file

@ -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 <NodeList.h>
#include <DependencyManager.h>
#include <DomainHandler.h>
#include <AddressManager.h>
static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000;
void ConnectionMonitor::init() {
// Connect to domain disconnected message
auto nodeList = DependencyManager::get<NodeList>();
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<AddressManager>();
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<DialogsManager>();
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();
}

View file

@ -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 <QObject>
#include <QTimer>
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

View file

@ -15,6 +15,7 @@
#include <AddressManager.h>
#include <DomainHandler.h>
#include <NodeList.h>
#include <steamworks-wrapper/SteamClient.h>
#include <UserActivityLogger.h>
#include <UUID.h>
@ -36,11 +37,11 @@ const QString SESSION_ID_KEY = "session_id";
void DiscoverabilityManager::updateLocation() {
auto accountManager = DependencyManager::get<AccountManager>();
if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
auto addressManager = DependencyManager::get<AddressManager>();
auto& domainHandler = DependencyManager::get<NodeList>()->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) {

View file

@ -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<NodeList>();
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);

View file

@ -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";

View file

@ -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 <mutex>
#include <QElapsedTimer>
#include <NumericalConstants.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <TextureCache.h>
#include <RenderArgs.h>
#include <ViewFrustum.h>
#include <render-utils/stars_vert.h>
#include <render-utils/stars_frag.h>
#include <render-utils/standardTransformPNTC_vert.h>
#include <render-utils/starsGrid_frag.h>
//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<StarVertex> 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<ModelCache>();
auto textureCache = DependencyManager::get<TextureCache>();
auto geometryCache = DependencyManager::get<GeometryCache>();
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);
}

View file

@ -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 <gpu/Context.h>
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

View file

@ -142,7 +142,7 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
mat4 legacyProjection = glm::ortho<float>(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);

View file

@ -98,6 +98,7 @@ Avatar::Avatar(RigPointer rig) :
_headData = static_cast<HeadData*>(new Head(this));
_skeletonModel = std::make_shared<SkeletonModel>(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<GeometryCache>()->bindSimpleProgram(batch, false, true, true, true);
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true);
DependencyManager::get<GeometryCache>()->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<Model> allocateAttachmentModel(bool isSoft, RigPointer rigOverride) {
if (isSoft) {

View file

@ -184,6 +184,8 @@ public slots:
glm::vec3 getRightPalmPosition() const;
glm::quat getRightPalmRotation() const;
void setModelURLFinished(bool success);
protected:
friend class AvatarManager;

View file

@ -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<AddressManager>().data(), &AddressManager::locationChangeRequired,
[=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){
goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation);
});
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
this, static_cast<SlotType>(&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;

View file

@ -8,6 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <thread>
#include <QCommandLineParser>
#include <QDebug>
#include <QDir>
@ -20,12 +22,13 @@
#include <gl/OpenGLVersionChecker.h>
#include <SharedUtil.h>
#include <steamworks-wrapper/SteamClient.h>
#include "AddressManager.h"
#include "Application.h"
#include "InterfaceLogging.h"
#include "UserActivityLogger.h"
#include "MainWindow.h"
#include <thread>
#ifdef HAS_BUGSPLAT
#include <BuildInfo.h>
@ -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

View file

@ -15,6 +15,9 @@
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
static AccountScriptingInterface sharedInstance;
auto accountManager = DependencyManager::get<AccountManager>();
QObject::connect(accountManager.data(), &AccountManager::profileChanged,
&sharedInstance, &AccountScriptingInterface::usernameChanged);
return &sharedInstance;
}

View file

@ -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();

View file

@ -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();
}

View file

@ -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:

View file

@ -28,6 +28,8 @@
#include "Util.h"
#include "ui/Stats.h"
#include "ui/AvatarInputs.h"
#include "OffscreenUi.h"
#include <QQmlContext>
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<float>(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<AudioScope>()->render(renderArgs, width, height);
@ -138,7 +142,7 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
mat4 legacyProjection = glm::ortho<float>(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<float>(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<OffscreenUi>()->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<TextureCache>()->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::mutex>;
std::queue<gpu::TexturePointer> _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);
}

View file

@ -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);

View file

@ -50,6 +50,10 @@ void DialogsManager::toggleAddressBar() {
emit addressBarToggled();
}
void DialogsManager::showAddressBar() {
AddressBarDialog::show();
}
void DialogsManager::toggleDiskCacheEditor() {
maybeCreateDialog(_diskCacheEditor);
_diskCacheEditor->toggle();

View file

@ -44,6 +44,7 @@ public:
public slots:
void toggleAddressBar();
void showAddressBar();
void toggleDiskCacheEditor();
void toggleLoginDialog();
void showLoginDialog();

View file

@ -12,8 +12,11 @@
#include "LoginDialog.h"
#include <QDesktopServices>
#include <QJsonDocument>
#include <QNetworkReply>
#include <NetworkingConstants.h>
#include <steamworks-wrapper/SteamClient.h>
#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<AccountManager>();
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<AccountManager>()->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<AccountManager>()->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>();
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>();
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<OffscreenUi>();
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());
}

View file

@ -16,35 +16,44 @@
#include <OffscreenQmlDialog.h>
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

View file

@ -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);

View file

@ -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();

View file

@ -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());
}

View file

@ -12,10 +12,11 @@
#define hifi_Base3DOverlay_h
#include <Transform.h>
#include <SpatiallyNestable.h>
#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;

View file

@ -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<glm::vec2> points;
QVector<glm::vec4> colors;

View file

@ -47,7 +47,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
auto geometryCache = DependencyManager::get<GeometryCache>();
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);

View file

@ -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<GeometryCache>()->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;

View file

@ -23,6 +23,7 @@ Line3DOverlay::Line3DOverlay() :
Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
Base3DOverlay(line3DOverlay),
_start(line3DOverlay->_start),
_end(line3DOverlay->_end),
_geometryCacheID(DependencyManager::get<GeometryCache>()->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<GeometryCache>();
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
}

View file

@ -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;

View file

@ -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());
}
}

View file

@ -39,6 +39,8 @@ public:
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
void locationChanged(bool tellPhysics) override;
private:
ModelPointer _model;

View file

@ -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") {

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -47,7 +47,7 @@ void Shape3DOverlay::render(RenderArgs* args) {
auto geometryCache = DependencyManager::get<GeometryCache>();
auto pipeline = args->_pipeline;
if (!pipeline) {
pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
}
transform.setScale(dimensions);

View file

@ -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<GeometryCache>();
auto pipeline = args->_pipeline;
if (!pipeline) {
pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
}
if (_isSolid) {

View file

@ -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<GeometryCache>()->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);
}

View file

@ -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));

View file

@ -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);

View file

@ -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);
}

View file

@ -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()

View file

@ -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 {

Some files were not shown because too many files have changed in this diff Show more