Merge remote-tracking branch 'upstream/master' into web_overlay_android

This commit is contained in:
Gabriel Calero 2018-06-29 15:09:32 -03:00
commit a6d093d8c9
129 changed files with 1655 additions and 1148 deletions

View file

@ -28,7 +28,7 @@ android {
'-DSTABLE_BUILD=' + STABLE_BUILD,
'-DDISABLE_QML=OFF',
'-DDISABLE_KTX_CACHE=OFF',
'-DUSE_BREAKPAD=' + (project.hasProperty("BACKTRACE_URL") && project.hasProperty("BACKTRACE_TOKEN") ? 'ON' : 'OFF');
'-DUSE_BREAKPAD=' + (System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_TOKEN") ? 'ON' : 'OFF');
}
}
signingConfigs {
@ -48,8 +48,8 @@ android {
buildTypes {
debug {
buildConfigField "String", "BACKTRACE_URL", "\"" + (project.hasProperty("BACKTRACE_URL") ? BACKTRACE_URL : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (project.hasProperty("BACKTRACE_TOKEN") ? BACKTRACE_TOKEN : '') + "\""
buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
}
release {
minifyEnabled false
@ -58,8 +58,8 @@ android {
project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") &&
project.hasProperty("HIFI_ANDROID_KEY_ALIAS") &&
project.hasProperty("HIFI_ANDROID_KEY_PASSWORD")? signingConfigs.release : null
buildConfigField "String", "BACKTRACE_URL", "\"" + (project.hasProperty("BACKTRACE_URL") ? BACKTRACE_URL : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (project.hasProperty("BACKTRACE_TOKEN") ? BACKTRACE_TOKEN : '') + "\""
buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
}
}
@ -74,6 +74,10 @@ android {
// so our merge has to depend on the external native build
variant.externalNativeBuildTasks.each { task ->
variant.mergeResources.dependsOn(task)
def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first()
def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first()
runDumpSymsTask.dependsOn(task)
variant.assemble.dependsOn(uploadDumpSymsTask)
}
variant.mergeAssets.doLast {

View file

@ -247,7 +247,7 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en
env->ReleaseStringUTFChars(username_, c_username);
env->ReleaseStringUTFChars(password_, c_password);
auto accountManager = AndroidHelper::instance().getAccountManager();
auto accountManager = DependencyManager::get<AccountManager>();
__loginCompletedListener = QAndroidJniObject(instance);
__usernameChangedListener = QAndroidJniObject(usernameChangedListener);
@ -289,18 +289,18 @@ Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(J
}
JNIEXPORT jboolean JNICALL
Java_io_highfidelity_hifiinterface_MainActivity_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
return AndroidHelper::instance().getAccountManager()->isLoggedIn();
return DependencyManager::get<AccountManager>()->isLoggedIn();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_MainActivity_nativeLogout(JNIEnv *env, jobject instance) {
AndroidHelper::instance().getAccountManager()->logout();
DependencyManager::get<AccountManager>()->logout();
}
JNIEXPORT jstring JNICALL
Java_io_highfidelity_hifiinterface_MainActivity_nativeGetDisplayName(JNIEnv *env,
jobject instance) {
QString username = AndroidHelper::instance().getAccountManager()->getAccountInfo().getUsername();
QString username = DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();
return env->NewStringUTF(username.toLatin1().data());
}

View file

@ -21,6 +21,7 @@ buildscript {
plugins {
id 'de.undercouch.download' version '3.3.0'
id "cz.malohlava" version "1.0.3"
id "io.github.http-builder-ng.http-plugin" version "0.1.1"
}
allprojects {
@ -67,6 +68,7 @@ def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def appDir = new File(projectDir, 'app')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms")
def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
@ -151,11 +153,11 @@ def packages = [
checksum: '14b02795d774457a33bbc60e00a786bc'
],
breakpad: [
file: 'breakpad.zip',
versionId: '2OwvCCZrF171wnte5T44AnjTYFhhJsGJ',
checksum: 'a46062a3167dfedd4fb4916136e204d2',
file: 'breakpad.tgz',
versionId: '8VrYXz7oyc.QBxNia0BVJOUBvrFO61jI',
checksum: 'ddcb23df336b08017042ba4786db1d9e',
sharedLibFolder: 'lib',
includeLibs: ['libbreakpad_client.a','libbreakpad.a']
includeLibs: ['libbreakpad_client.a']
]
]
@ -549,7 +551,93 @@ task cleanDependencies(type: Delete) {
delete 'app/src/main/res/values/libs.xml'
}
def runBreakpadDumpSyms = { buildType ->
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
def objDir = new File("${appDir}/build/intermediates/cmake/${buildType}/obj/arm64-v8a")
def stripDebugSymbol = "${appDir}/build/intermediates/transforms/stripDebugSymbol/${buildType}/0/lib/arm64-v8a/"
def outputDir = new File(breakpadDumpSymsDir, buildType)
if (!outputDir.exists()) {
outputDir.mkdirs()
}
objDir.eachFileRecurse (FileType.FILES) { file ->
if (file.name.endsWith('.so')) {
def output = file.name + ".sym"
def cmdArgs = [
file.toString(),
stripDebugSymbol
]
def result = exec {
workingDir HIFI_ANDROID_PRECOMPILED + '/breakpad/bin'
commandLine './dump_syms'
args cmdArgs
ignoreExitValue true
standardOutput = new BufferedOutputStream(new FileOutputStream(new File(outputDir, output)))
}
}
}
}
task runBreakpadDumpSymsDebug() {
doLast {
runBreakpadDumpSyms("debug");
}
}
task runBreakpadDumpSymsRelease() {
doLast {
runBreakpadDumpSyms("release");
}
}
task zipDumpSymsDebug(type: Zip, dependsOn: runBreakpadDumpSymsDebug) {
from (new File(breakpadDumpSymsDir, "debug").absolutePath)
archiveName "symbols-${RELEASE_NUMBER}-debug.zip"
destinationDir(new File("${appDir}/build/tmp/"))
}
task zipDumpSymsRelease(type: Zip, dependsOn: runBreakpadDumpSymsRelease) {
from (new File(breakpadDumpSymsDir, "release").absolutePath)
archiveName "symbols-${RELEASE_NUMBER}-release.zip"
destinationDir(new File("${appDir}/build/tmp/"))
}
task uploadBreakpadDumpSymsDebug(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsDebug) {
onlyIf {
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
}
config {
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
}
post {
request.uri.path = '/post'
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-debug.zip").bytes
request.contentType = 'application/octet-stream'
response.success {
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-debug.zip uploaded")
}
}
}
task uploadBreakpadDumpSymsRelease(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsRelease) {
onlyIf {
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
}
config {
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
}
post {
request.uri.path = '/post'
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-release.zip").bytes
request.contentType = 'application/octet-stream'
response.success {
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-release.zip uploaded")
}
}
}
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// See the comment on the qtBundle task above

View file

@ -504,6 +504,11 @@ void EntityServer::startDynamicDomainVerification() {
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
if (jsonObject["domain_id"].toString() != thisDomainID) {
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
networkReply->deleteLater();
return;
}
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;

View file

@ -660,9 +660,8 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username, bool isOpti
// even if we have a public key for them right now, request a new one in case it has just changed
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "publicKeyJSONCallback";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "publicKeyJSONErrorCallback";
@ -675,19 +674,19 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username, bool isOpti
QNetworkAccessManager::GetOperation, callbackParams);
}
QString extractUsernameFromPublicKeyRequest(QNetworkReply& requestReply) {
QString extractUsernameFromPublicKeyRequest(QNetworkReply* requestReply) {
// extract the username from the request url
QString username;
const QString PUBLIC_KEY_URL_REGEX_STRING = "api\\/v1\\/users\\/([A-Za-z0-9_\\.]+)\\/public_key";
QRegExp usernameRegex(PUBLIC_KEY_URL_REGEX_STRING);
if (usernameRegex.indexIn(requestReply.url().toString()) != -1) {
if (usernameRegex.indexIn(requestReply->url().toString()) != -1) {
username = usernameRegex.cap(1);
}
return username.toLower();
}
void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply* requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
QString username = extractUsernameFromPublicKeyRequest(requestReply);
bool isOptimisticKey = _inFlightPublicKeyRequests.take(username);
@ -707,8 +706,8 @@ void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) {
}
}
void DomainGatekeeper::publicKeyJSONErrorCallback(QNetworkReply& requestReply) {
qDebug() << "publicKey api call failed:" << requestReply.error();
void DomainGatekeeper::publicKeyJSONErrorCallback(QNetworkReply* requestReply) {
qDebug() << "publicKey api call failed:" << requestReply->error();
QString username = extractUsernameFromPublicKeyRequest(requestReply);
_inFlightPublicKeyRequests.remove(username);
}
@ -893,9 +892,8 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "getIsGroupMemberJSONCallback";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback";
const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/members/%2";
@ -906,18 +904,18 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
}
QString extractUsernameFromGroupMembershipsReply(QNetworkReply& requestReply) {
QString extractUsernameFromGroupMembershipsReply(QNetworkReply* requestReply) {
// extract the username from the request url
QString username;
const QString GROUP_MEMBERSHIPS_URL_REGEX_STRING = "api\\/v1\\/groups\\/members\\/([A-Za-z0-9_\\.]+)";
QRegExp usernameRegex(GROUP_MEMBERSHIPS_URL_REGEX_STRING);
if (usernameRegex.indexIn(requestReply.url().toString()) != -1) {
if (usernameRegex.indexIn(requestReply->url().toString()) != -1) {
username = usernameRegex.cap(1);
}
return username.toLower();
}
void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply) {
void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply* requestReply) {
// {
// "data":{
// "username":"sethalves",
@ -934,7 +932,7 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply)
// "status":"success"
// }
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
if (jsonObject["status"].toString() == "success") {
QJsonObject data = jsonObject["data"].toObject();
QJsonObject groups = data["groups"].toObject();
@ -953,16 +951,15 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply& requestReply)
_inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply));
}
void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply& requestReply) {
qDebug() << "getIsGroupMember api call failed:" << requestReply.error();
void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply* requestReply) {
qDebug() << "getIsGroupMember api call failed:" << requestReply->error();
_inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply));
}
void DomainGatekeeper::getDomainOwnerFriendsList() {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "getDomainOwnerFriendsListJSONCallback";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "getDomainOwnerFriendsListErrorCallback";
const QString GET_FRIENDS_LIST_PATH = "api/v1/user/friends";
@ -974,7 +971,7 @@ void DomainGatekeeper::getDomainOwnerFriendsList() {
}
void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply) {
void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply* requestReply) {
// {
// status: "success",
// data: {
@ -991,7 +988,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requ
// ]
// }
// }
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
if (jsonObject["status"].toString() == "success") {
_domainOwnerFriends.clear();
QJsonArray friends = jsonObject["data"].toObject()["friends"].toArray();
@ -1003,8 +1000,8 @@ void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requ
}
}
void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply& requestReply) {
qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply.error();
void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply* requestReply) {
qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply->error();
}
void DomainGatekeeper::refreshGroupsCache() {

View file

@ -51,14 +51,14 @@ public slots:
void processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message);
void processICEPeerInformationPacket(QSharedPointer<ReceivedMessage> message);
void publicKeyJSONCallback(QNetworkReply& requestReply);
void publicKeyJSONErrorCallback(QNetworkReply& requestReply);
void publicKeyJSONCallback(QNetworkReply* requestReply);
void publicKeyJSONErrorCallback(QNetworkReply* requestReply);
void getIsGroupMemberJSONCallback(QNetworkReply& requestReply);
void getIsGroupMemberErrorCallback(QNetworkReply& requestReply);
void getIsGroupMemberJSONCallback(QNetworkReply* requestReply);
void getIsGroupMemberErrorCallback(QNetworkReply* requestReply);
void getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply);
void getDomainOwnerFriendsListErrorCallback(QNetworkReply& requestReply);
void getDomainOwnerFriendsListJSONCallback(QNetworkReply* requestReply);
void getDomainOwnerFriendsListErrorCallback(QNetworkReply* requestReply);
void refreshGroupsCache();

View file

@ -514,13 +514,13 @@ void DomainServer::getTemporaryName(bool force) {
// request a temporary name from the metaverse
auto accountManager = DependencyManager::get<AccountManager>();
JSONCallbackParameters callbackParameters { this, "handleTempDomainSuccess", this, "handleTempDomainError" };
JSONCallbackParameters callbackParameters { this, "handleTempDomainSuccess", "handleTempDomainError" };
accountManager->sendRequest("/api/v1/domains/temporary", AccountManagerAuth::None,
QNetworkAccessManager::PostOperation, callbackParameters);
}
void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
void DomainServer::handleTempDomainSuccess(QNetworkReply* requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
// grab the information for the new domain
static const QString DATA_KEY = "data";
@ -565,7 +565,7 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) {
}
}
void DomainServer::handleTempDomainError(QNetworkReply& requestReply) {
void DomainServer::handleTempDomainError(QNetworkReply* requestReply) {
qWarning() << "A temporary name was requested but there was an error creating one. Please try again via domain-server relaunch"
<< "or from the domain-server settings.";
}
@ -1345,7 +1345,7 @@ void DomainServer::sendPendingTransactionsToServer() {
JSONCallbackParameters transactionCallbackParams;
transactionCallbackParams.jsonCallbackReceiver = this;
transactionCallbackParams.callbackReceiver = this;
transactionCallbackParams.jsonCallbackMethod = "transactionJSONCallback";
while (i != _pendingAssignmentCredits.end()) {
@ -1449,11 +1449,11 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
DependencyManager::get<AccountManager>()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())),
AccountManagerAuth::Optional,
QNetworkAccessManager::PutOperation,
JSONCallbackParameters(nullptr, QString(), this, "handleMetaverseHeartbeatError"),
JSONCallbackParameters(this, QString(), "handleMetaverseHeartbeatError"),
domainUpdateJSON.toUtf8());
}
void DomainServer::handleMetaverseHeartbeatError(QNetworkReply& requestReply) {
void DomainServer::handleMetaverseHeartbeatError(QNetworkReply* requestReply) {
if (!_metaverseHeartbeatTimer) {
// avoid rehandling errors from the same issue
return;
@ -1462,13 +1462,13 @@ void DomainServer::handleMetaverseHeartbeatError(QNetworkReply& requestReply) {
// 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()) {
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();
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY];
if (!tokenFailure.isNull()) {
@ -1531,9 +1531,8 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
// make sure we hear about failure so we can retry
JSONCallbackParameters callbackParameters;
callbackParameters.errorCallbackReceiver = this;
callbackParameters.callbackReceiver = this;
callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate";
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.jsonCallbackMethod = "handleSuccessfulICEServerAddressUpdate";
static bool printedIceServerMessage = false;
@ -1552,7 +1551,7 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
domainUpdateJSON.toUtf8());
}
void DomainServer::handleSuccessfulICEServerAddressUpdate(QNetworkReply& requestReply) {
void DomainServer::handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply) {
_sendICEServerAddressToMetaverseAPIInProgress = false;
if (_sendICEServerAddressToMetaverseAPIRedo) {
qDebug() << "ice-server address updated with metaverse, but has since changed. redoing update...";
@ -1563,7 +1562,7 @@ void DomainServer::handleSuccessfulICEServerAddressUpdate(QNetworkReply& request
}
}
void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply& requestReply) {
void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply* requestReply) {
_sendICEServerAddressToMetaverseAPIInProgress = false;
if (_sendICEServerAddressToMetaverseAPIRedo) {
// if we have new data, retry right away, even though the previous attempt didn't go well.
@ -1573,7 +1572,7 @@ void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply& requestRepl
const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000;
qWarning() << "Failed to update ice-server address with High Fidelity Metaverse - error was"
<< requestReply.errorString();
<< requestReply->errorString();
qWarning() << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds";
QTimer::singleShot(ICE_SERVER_UPDATE_RETRY_MS, this, SLOT(sendICEServerAddressToMetaverseAPI()));

View file

@ -108,10 +108,10 @@ private slots:
void sendHeartbeatToIceServer();
void handleConnectedNode(SharedNodePointer newNode);
void handleTempDomainSuccess(QNetworkReply& requestReply);
void handleTempDomainError(QNetworkReply& requestReply);
void handleTempDomainSuccess(QNetworkReply* requestReply);
void handleTempDomainError(QNetworkReply* requestReply);
void handleMetaverseHeartbeatError(QNetworkReply& requestReply);
void handleMetaverseHeartbeatError(QNetworkReply* requestReply);
void queuedQuit(QString quitMessage, int exitCode);
@ -121,8 +121,8 @@ private slots:
void handleICEHostInfo(const QHostInfo& hostInfo);
void sendICEServerAddressToMetaverseAPI();
void handleSuccessfulICEServerAddressUpdate(QNetworkReply& requestReply);
void handleFailedICEServerAddressUpdate(QNetworkReply& requestReply);
void handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply);
void handleFailedICEServerAddressUpdate(QNetworkReply* requestReply);
void updateReplicatedNodes();
void updateDownstreamNodes();

View file

@ -1815,9 +1815,8 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() {
void DomainServerSettingsManager::apiGetGroupID(const QString& groupName) {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "apiGetGroupIDJSONCallback";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "apiGetGroupIDErrorCallback";
const QString GET_GROUP_ID_PATH = "api/v1/groups/names/%1";
@ -1826,7 +1825,7 @@ void DomainServerSettingsManager::apiGetGroupID(const QString& groupName) {
QNetworkAccessManager::GetOperation, callbackParams);
}
void DomainServerSettingsManager::apiGetGroupIDJSONCallback(QNetworkReply& requestReply) {
void DomainServerSettingsManager::apiGetGroupIDJSONCallback(QNetworkReply* requestReply) {
// {
// "data":{
// "groups":[{
@ -1857,7 +1856,7 @@ void DomainServerSettingsManager::apiGetGroupIDJSONCallback(QNetworkReply& reque
// },
// "status":"success"
// }
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
if (jsonObject["status"].toString() == "success") {
QJsonArray groups = jsonObject["data"].toObject()["groups"].toArray();
for (int i = 0; i < groups.size(); i++) {
@ -1876,15 +1875,14 @@ void DomainServerSettingsManager::apiGetGroupIDJSONCallback(QNetworkReply& reque
}
}
void DomainServerSettingsManager::apiGetGroupIDErrorCallback(QNetworkReply& requestReply) {
qDebug() << "******************** getGroupID api call failed:" << requestReply.error();
void DomainServerSettingsManager::apiGetGroupIDErrorCallback(QNetworkReply* requestReply) {
qDebug() << "******************** getGroupID api call failed:" << requestReply->error();
}
void DomainServerSettingsManager::apiGetGroupRanks(const QUuid& groupID) {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "apiGetGroupRanksJSONCallback";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "apiGetGroupRanksErrorCallback";
const QString GET_GROUP_RANKS_PATH = "api/v1/groups/%1/ranks";
@ -1893,7 +1891,7 @@ void DomainServerSettingsManager::apiGetGroupRanks(const QUuid& groupID) {
QNetworkAccessManager::GetOperation, callbackParams);
}
void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& requestReply) {
void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply* requestReply) {
// {
// "data":{
// "groups":{
@ -1926,7 +1924,7 @@ void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& re
// }
bool changed = false;
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
if (jsonObject["status"].toString() == "success") {
QJsonObject groups = jsonObject["data"].toObject()["groups"].toObject();
@ -1972,8 +1970,8 @@ void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply& re
}
}
void DomainServerSettingsManager::apiGetGroupRanksErrorCallback(QNetworkReply& requestReply) {
qDebug() << "******************** getGroupRanks api call failed:" << requestReply.error();
void DomainServerSettingsManager::apiGetGroupRanksErrorCallback(QNetworkReply* requestReply) {
qDebug() << "******************** getGroupRanks api call failed:" << requestReply->error();
}
void DomainServerSettingsManager::recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID) {

View file

@ -133,10 +133,10 @@ signals:
void settingsUpdated();
public slots:
void apiGetGroupIDJSONCallback(QNetworkReply& requestReply);
void apiGetGroupIDErrorCallback(QNetworkReply& requestReply);
void apiGetGroupRanksJSONCallback(QNetworkReply& requestReply);
void apiGetGroupRanksErrorCallback(QNetworkReply& requestReply);
void apiGetGroupIDJSONCallback(QNetworkReply* requestReply);
void apiGetGroupIDErrorCallback(QNetworkReply* requestReply);
void apiGetGroupRanksJSONCallback(QNetworkReply* requestReply);
void apiGetGroupRanksErrorCallback(QNetworkReply* requestReply);
private slots:
void processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message);

View file

@ -126,10 +126,12 @@ Item {
activeFocusOnPress: true
ShortcutText {
z: 10
anchors {
verticalCenter: usernameField.textFieldLabel.verticalCenter
left: usernameField.textFieldLabel.right
leftMargin: 10
left: usernameField.left
top: usernameField.top
leftMargin: usernameField.textFieldLabel.contentWidth + 10
topMargin: -19
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
@ -154,10 +156,12 @@ Item {
activeFocusOnPress: true
ShortcutText {
z: 10
anchors {
verticalCenter: passwordField.textFieldLabel.verticalCenter
left: passwordField.textFieldLabel.right
leftMargin: 10
left: passwordField.left
top: passwordField.top
leftMargin: passwordField.textFieldLabel.contentWidth + 10
topMargin: -19
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
@ -168,6 +172,7 @@ Item {
onLinkActivated: loginDialog.openUrl(link)
}
onFocusChanged: {
root.text = "";
root.isPassword = true;

View file

@ -129,12 +129,10 @@ Rectangle {
id: stereoMic
spacing: muteMic.spacing;
text: qsTr("Enable stereo input");
checked: AudioScriptingInterface.isStereoInput();
checked: AudioScriptingInterface.isStereoInput;
onClicked: {
var success = AudioScriptingInterface.setStereoInput(checked);
if (!success) {
checked = !checked;
}
AudioScriptingInterface.isStereoInput = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.isStereoInput; }); // restore binding
}
}
}

View file

@ -646,6 +646,7 @@ Item {
height: 40;
enabled: root.hasPermissionToRezThis &&
MyAvatar.skeletonModelURL !== root.itemHref &&
!root.wornEntityID &&
root.valid;
onHoveredChanged: {

View file

@ -957,7 +957,7 @@ Rectangle {
function updateCurrentlyWornWearables(wearables) {
for (var i = 0; i < purchasesModel.count; i++) {
for (var j = 0; j < wearables.length; j++) {
if (purchasesModel.get(i).itemType === "wearable" &&
if (purchasesModel.get(i).item_type === "wearable" &&
wearables[j].entityCertID === purchasesModel.get(i).certificate_id &&
wearables[j].entityEdition.toString() === purchasesModel.get(i).edition_number) {
purchasesModel.setProperty(i, 'wornEntityID', wearables[j].entityID);

View file

@ -253,7 +253,11 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta
} else if (link === "#blockchain") {
Qt.openUrlExternally("https://docs.highfidelity.com/high-fidelity-commerce");
} else if (link === "#bank") {
Qt.openUrlExternally("hifi://BankOfHighFidelity");
if ((Account.metaverseServerURL).toString().indexOf("staging") >= 0) {
Qt.openUrlExternally("hifi://hifiqa-master-metaverse-staging"); // So that we can test in staging.
} else {
Qt.openUrlExternally("hifi://BankOfHighFidelity");
}
} else if (link === "#support") {
Qt.openUrlExternally("mailto:support@highfidelity.com");
}

View file

@ -252,64 +252,6 @@ Item {
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Item {
// On empty history. We don't want to flash and then replace, so don't show until we know we should.
// The history is empty when it contains 1 item (the pending item count) AND there are no pending items.
visible: transactionHistoryModel.count === 1 &&
transactionHistoryModel.retrievedAtLeastOnePage &&
transactionHistoryModel.get(0).count === 0;
anchors.centerIn: parent;
width: parent.width - 12;
height: parent.height;
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
RalewayRegular {
id: noActivityText;
text: "Congrats! Your wallet is all set!<br><br>" +
"<b>Where's my HFC?</b><br>" +
"High Fidelity commerce is in open beta right now. Want more HFC? Get it by meeting with a banker at " +
"<a href='#goToBank'>BankOfHighFidelity</a>!"
// Text size
size: 22;
// Style
color: hifi.colors.blueAccent;
anchors.top: parent.top;
anchors.topMargin: 36;
anchors.left: parent.left;
anchors.leftMargin: 12;
anchors.right: parent.right;
anchors.rightMargin: 12;
height: paintedHeight;
wrapMode: Text.WordWrap;
horizontalAlignment: Text.AlignHCenter;
onLinkActivated: {
sendSignalToWallet({ method: "transactionHistory_goToBank" });
}
}
HifiControlsUit.Button {
id: bankButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: noActivityText.bottom;
anchors.topMargin: 30;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "VISIT BANK OF HIGH FIDELITY";
onClicked: {
sendSignalToWallet({ method: "transactionHistory_goToBank" });
}
}
}
ListView {
id: transactionHistory;
@ -411,6 +353,64 @@ Item {
}
}
}
Item {
// On empty history. We don't want to flash and then replace, so don't show until we know we should.
// The history is empty when it contains 1 item (the pending item count) AND there are no pending items.
visible: transactionHistoryModel.count === 1 &&
transactionHistoryModel.retrievedAtLeastOnePage &&
transactionHistoryModel.get(0).count === 0;
anchors.centerIn: parent;
width: parent.width - 12;
height: parent.height;
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
RalewayRegular {
id: noActivityText;
text: "Congrats! Your wallet is all set!<br><br>" +
"<b>Where's my HFC?</b><br>" +
"High Fidelity commerce is in open beta right now. Want more HFC? Get it by meeting with a banker at " +
"<a href='#goToBank'>BankOfHighFidelity</a>!"
// Text size
size: 22;
// Style
color: hifi.colors.blueAccent;
anchors.top: parent.top;
anchors.topMargin: 36;
anchors.left: parent.left;
anchors.leftMargin: 12;
anchors.right: parent.right;
anchors.rightMargin: 12;
height: paintedHeight;
wrapMode: Text.WordWrap;
horizontalAlignment: Text.AlignHCenter;
onLinkActivated: {
sendSignalToWallet({ method: "transactionHistory_goToBank" });
}
}
HifiControlsUit.Button {
id: bankButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: noActivityText.bottom;
anchors.topMargin: 30;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "VISIT BANK OF HIGH FIDELITY";
onClicked: {
sendSignalToWallet({ method: "transactionHistory_goToBank" });
}
}
}
}
}

View file

@ -17,7 +17,7 @@ PreferencesDialog {
id: root
objectName: "GeneralPreferencesDialog"
title: "General Settings"
showCategories: ["User Interface", "HMD", "Snapshots", "Privacy"]
showCategories: ["User Interface", "Mouse Sensitivity", "HMD", "Snapshots", "Privacy"]
property var settings: Settings {
category: root.objectName
property alias x: root.x

View file

@ -32,6 +32,6 @@ StackView {
TabletPreferencesDialog {
id: root
objectName: "TabletGeneralPreferences"
showCategories: ["User Interface", "HMD", "Snapshots", "Privacy"]
showCategories: ["User Interface", "Mouse Sensitivity", "HMD", "Snapshots", "Privacy"]
}
}

View file

@ -10,7 +10,6 @@
//
#include "AndroidHelper.h"
#include <QDebug>
#include <AccountManager.h>
#include "Application.h"
#if defined(qApp)
@ -22,25 +21,6 @@ AndroidHelper::AndroidHelper() {
}
AndroidHelper::~AndroidHelper() {
workerThread.quit();
workerThread.wait();
}
void AndroidHelper::init() {
workerThread.start();
_accountManager = QSharedPointer<AccountManager>(new AccountManager, &QObject::deleteLater);
_accountManager->setIsAgent(true);
_accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
_accountManager->setSessionID(DependencyManager::get<AccountManager>()->getSessionID());
connect(_accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) {
DependencyManager::get<AccountManager>()->setAccountInfo(AndroidHelper::instance().getAccountManager()->getAccountInfo());
DependencyManager::get<AccountManager>()->setAuthURL(authURL);
});
connect(_accountManager.data(), &AccountManager::logoutComplete, [] () {
DependencyManager::get<AccountManager>()->logout();
});
_accountManager->moveToThread(&workerThread);
}
void AndroidHelper::requestActivity(const QString &activityName, const bool backToScene, QList<QString> args) {

View file

@ -13,8 +13,6 @@
#define hifi_Android_Helper_h
#include <QObject>
#include <QThread>
#include <AccountManager.h>
class AndroidHelper : public QObject {
Q_OBJECT
@ -23,7 +21,6 @@ public:
static AndroidHelper instance;
return instance;
}
void init();
void requestActivity(const QString &activityName, const bool backToScene, QList<QString> args = QList<QString>());
void notifyLoadComplete();
void notifyEnterForeground();
@ -32,7 +29,6 @@ public:
void performHapticFeedback(int duration);
void processURL(const QString &url);
QSharedPointer<AccountManager> getAccountManager() { return _accountManager; }
AndroidHelper(AndroidHelper const&) = delete;
void operator=(AndroidHelper const&) = delete;
@ -50,8 +46,6 @@ signals:
private:
AndroidHelper();
~AndroidHelper();
QSharedPointer<AccountManager> _accountManager;
QThread workerThread;
};
#endif

View file

@ -1081,6 +1081,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
auto nodeList = DependencyManager::get<NodeList>();
nodeList->startThread();
// move the AddressManager to the NodeList thread so that domain resets due to domain changes always occur
// before we tell MyAvatar to go to a new location in the new domain
auto addressManager = DependencyManager::get<AddressManager>();
addressManager->moveToThread(nodeList->thread());
const char** constArgv = const_cast<const char**>(argv);
if (cmdOptionExists(argc, constArgv, "--disableWatchdog")) {
DISABLE_WATCHDOG = true;
@ -1231,8 +1236,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
accountManager->setIsAgent(true);
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
auto addressManager = DependencyManager::get<AddressManager>();
// use our MyAvatar position and quat for address manager path
addressManager->setPositionGetter([this]{ return getMyAvatar()->getWorldPosition(); });
addressManager->setOrientationGetter([this]{ return getMyAvatar()->getWorldOrientation(); });
@ -2256,7 +2259,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
#if defined(Q_OS_ANDROID)
AndroidHelper::instance().init();
connect(&AndroidHelper::instance(), &AndroidHelper::enterBackground, this, &Application::enterBackground);
connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground);
AndroidHelper::instance().notifyLoadComplete();
@ -2294,6 +2296,7 @@ void Application::domainConnectionRefused(const QString& reasonMessage, int reas
QString message = "Unable to connect to the location you are visiting.\n";
message += reasonMessage;
OffscreenUi::asyncWarning("", message);
getMyAvatar()->setWorldVelocity(glm::vec3(0.0f));
break;
}
default:

View file

@ -24,6 +24,7 @@
#include <QtAndroidExtras/QAndroidJniObject>
#include <SettingHelpers.h>
#include <BuildInfo.h>
google_breakpad::ExceptionHandler* gBreakpadHandler;
@ -43,7 +44,23 @@ QString obbDir() {
return dataAbsPath;
}
void flushAnnotations() {
QSettings settings(obbDir() + "/annotations.json", JSON_FORMAT);
settings.clear();
settings.beginGroup("Annotations");
for (auto k : annotations.keys()) {
settings.setValue(k, annotations.value(k));
}
settings.endGroup();
settings.sync();
}
bool startCrashHandler() {
annotations["version"] = BuildInfo::VERSION;
annotations["build_number"] = BuildInfo::BUILD_NUMBER;
annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING;
flushAnnotations();
gBreakpadHandler = new google_breakpad::ExceptionHandler(
google_breakpad::MinidumpDescriptor(obbDir().toStdString()),
@ -56,15 +73,7 @@ void setCrashAnnotation(std::string name, std::string value) {
QString qName = QString::fromStdString(name);
QString qValue = QString::fromStdString(value);
annotations[qName] = qValue;
QSettings settings(obbDir() + "/annotations.json", JSON_FORMAT);
settings.clear();
settings.beginGroup("Annotations");
for (auto k : annotations.keys()) {
settings.setValue(k, annotations.value(k));
}
settings.endGroup();
settings.sync();
flushAnnotations();
}
#endif

View file

@ -97,7 +97,7 @@ void DiscoverabilityManager::updateLocation() {
locationObject.insert(AVAILABILITY_KEY_IN_LOCATION, findableByString(static_cast<Discoverability::Mode>(_mode.get())));
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.callbackReceiver = this;
callbackParameters.jsonCallbackMethod = "handleHeartbeatResponse";
// figure out if we'll send a fresh location or just a simple heartbeat
@ -121,7 +121,7 @@ void DiscoverabilityManager::updateLocation() {
// we still send a heartbeat to the metaverse server for stats collection
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.callbackReceiver = this;
callbackParameters.jsonCallbackMethod = "handleHeartbeatResponse";
accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
@ -136,7 +136,7 @@ void DiscoverabilityManager::updateLocation() {
setCrashAnnotation("address", currentAddress.toString().toStdString());
}
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply* requestReply) {
auto dataObject = AccountManager::dataObjectFromResponse(requestReply);
if (!dataObject.isEmpty()) {

View file

@ -51,7 +51,7 @@ public:
static QString findableByString(Discoverability::Mode discoverabilityMode);
private slots:
void handleHeartbeatResponse(QNetworkReply& requestReply);
void handleHeartbeatResponse(QNetworkReply* requestReply);
private:
DiscoverabilityManager();

View file

@ -283,7 +283,7 @@ Menu::Menu() {
MenuWrapper* settingsMenu = addMenu("Settings");
// Settings > General...
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_G, nullptr, nullptr, QAction::PreferencesRole);
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_G, nullptr, nullptr);
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/GeneralPreferencesDialog.qml"),
QString("hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog");

View file

@ -11,25 +11,16 @@
#include "SecondaryCamera.h"
#include <RenderDeferredTask.h>
#include <RenderForwardTask.h>
#include <glm/gtx/transform.hpp>
#include <gpu/Context.h>
#include <TextureCache.h>
#include "Application.h"
using RenderArgsPointer = std::shared_ptr<RenderArgs>;
void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (!isDeferred) {
task.addJob<RenderForwardTask>("Forward", items);
} else {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
}
class SecondaryCameraJob { // Changes renderContext for our framebuffer and view.
public:
using Config = SecondaryCameraJobConfig;
@ -213,10 +204,10 @@ void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inp
const auto cachedArg = task.addJob<SecondaryCameraJob>("SecondaryCamera");
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (!isDeferred) {
task.addJob<RenderForwardTask>("Forward", items);
if (isDeferred) {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items, false);
} else {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
task.addJob<RenderForwardTask>("Forward", items);
}
task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
}

View file

@ -12,23 +12,11 @@
#pragma once
#ifndef hifi_SecondaryCamera_h
#define hifi_SecondaryCamera_h
#include <RenderShadowTask.h>
#include <render/RenderFetchCullSortTask.h>
#include <RenderDeferredTask.h>
#include <RenderForwardTask.h>
#include <TextureCache.h>
#include <ViewFrustum.h>
class MainRenderTask {
public:
using JobModel = render::Task::Model<MainRenderTask>;
MainRenderTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true);
};
class SecondaryCameraJobConfig : public render::Task::Config { // Exposes secondary camera parameters to JavaScript.
Q_OBJECT
Q_PROPERTY(QUuid attachedEntityId MEMBER attachedEntityId NOTIFY dirty) // entity whose properties define camera position and orientation

View file

@ -36,13 +36,13 @@
#include <SettingHandle.h>
#include <UsersScriptingInterface.h>
#include <UUID.h>
#include <avatars-renderer/OtherAvatar.h>
#include <shared/ConicalViewFrustum.h>
#include "Application.h"
#include "InterfaceLogging.h"
#include "Menu.h"
#include "MyAvatar.h"
#include "OtherAvatar.h"
#include "SceneScriptingInterface.h"
// 50 times per second - target is 45hz, but this helps account for any small deviations
@ -186,11 +186,21 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
uint64_t updateExpiry = startTime + UPDATE_BUDGET;
int numAvatarsUpdated = 0;
int numAVatarsNotUpdated = 0;
bool physicsEnabled = qApp->isPhysicsEnabled();
render::Transaction transaction;
while (!sortedAvatars.empty()) {
const SortableAvatar& sortData = sortedAvatars.top();
const auto avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
const auto otherAvatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar());
// if the geometry is loaded then turn off the orb
if (avatar->getSkeletonModel()->isLoaded()) {
// remove the orb if it is there
otherAvatar->removeOrb();
} else {
otherAvatar->updateOrbPosition();
}
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
if (ignoring) {
@ -202,7 +212,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (_shouldRender) {
avatar->ensureInScene(avatar, qApp->getMain3DScene());
}
if (!avatar->isInPhysicsSimulation()) {
if (physicsEnabled && !avatar->isInPhysicsSimulation()) {
ShapeInfo shapeInfo;
avatar->computeShapeInfo(shapeInfo);
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));

View file

@ -154,6 +154,10 @@ const QUuid AvatarMotionState::getObjectID() const {
return _avatar->getSessionUUID();
}
QString AvatarMotionState::getName() const {
return _avatar->getName();
}
// virtual
QUuid AvatarMotionState::getSimulatorID() const {
return _avatar->getSessionUUID();

View file

@ -59,6 +59,7 @@ public:
virtual const QUuid getObjectID() const override;
virtual QString getName() const override;
virtual QUuid getSimulatorID() const override;
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);

View file

@ -532,10 +532,14 @@ void MyAvatar::forgetChild(SpatiallyNestablePointer newChild) const {
SpatiallyNestable::forgetChild(newChild);
}
void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object) {
void MyAvatar::recalculateChildCauterization() const {
_cauterizationNeedsUpdate = true;
}
void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool cauterize) {
if (object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
entity->setCauterized(!_prevShouldDrawHead);
entity->setCauterized(cauterize);
}
}
@ -545,17 +549,42 @@ void MyAvatar::simulate(float deltaTime) {
animateScaleChanges(deltaTime);
if (_cauterizationNeedsUpdate) {
const std::unordered_set<int>& headBoneSet = _skeletonModel->getCauterizeBoneSet();
_cauterizationNeedsUpdate = false;
// Redisplay cauterized entities that are no longer children of the avatar.
auto cauterizedChild = _cauterizedChildrenOfHead.begin();
if (cauterizedChild != _cauterizedChildrenOfHead.end()) {
auto children = getChildren();
while (cauterizedChild != _cauterizedChildrenOfHead.end()) {
if (!children.contains(*cauterizedChild)) {
updateChildCauterization(*cauterizedChild, false);
cauterizedChild = _cauterizedChildrenOfHead.erase(cauterizedChild);
} else {
++cauterizedChild;
}
}
}
// Update cauterization of entities that are children of the avatar.
auto headBoneSet = _skeletonModel->getCauterizeBoneSet();
forEachChild([&](SpatiallyNestablePointer object) {
bool isChildOfHead = headBoneSet.find(object->getParentJointIndex()) != headBoneSet.end();
if (isChildOfHead) {
updateChildCauterization(object);
// Cauterize or display children of head per head drawing state.
updateChildCauterization(object, !_prevShouldDrawHead);
object->forEachDescendant([&](SpatiallyNestablePointer descendant) {
updateChildCauterization(descendant);
updateChildCauterization(descendant, !_prevShouldDrawHead);
});
_cauterizedChildrenOfHead.insert(object);
} else if (_cauterizedChildrenOfHead.find(object) != _cauterizedChildrenOfHead.end()) {
// Redisplay cauterized children that are not longer children of the head.
updateChildCauterization(object, false);
object->forEachDescendant([&](SpatiallyNestablePointer descendant) {
updateChildCauterization(descendant, false);
});
_cauterizedChildrenOfHead.erase(object);
}
});
_cauterizationNeedsUpdate = false;
}
{
@ -685,7 +714,8 @@ void MyAvatar::simulate(float deltaTime) {
entityTree->recurseTreeWithOperator(&moveOperator);
}
});
_characterController.setFlyingAllowed(zoneAllowsFlying && _enableFlying);
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
_characterController.setFlyingAllowed(zoneAllowsFlying && (_enableFlying || !isPhysicsEnabled));
_characterController.setCollisionlessAllowed(collisionlessAllowed);
}
@ -1068,8 +1098,8 @@ void MyAvatar::saveData() {
settings.setValue("displayName", _displayName);
settings.setValue("collisionSoundURL", _collisionSoundURL);
settings.setValue("useSnapTurn", _useSnapTurn);
settings.setValue("clearOverlayWhenMoving", _clearOverlayWhenMoving);
settings.setValue("userHeight", getUserHeight());
settings.setValue("enabledFlying", getFlyingEnabled());
settings.endGroup();
}
@ -1219,11 +1249,10 @@ void MyAvatar::loadData() {
settings.remove("avatarEntityData");
}
setAvatarEntityDataChanged(true);
setFlyingEnabled(settings.value("enabledFlying").toBool());
setDisplayName(settings.value("displayName").toString());
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool());
setClearOverlayWhenMoving(settings.value("clearOverlayWhenMoving", _clearOverlayWhenMoving).toBool());
setDominantHand(settings.value("dominantHand", _dominantHand).toString().toLower());
setUserHeight(settings.value("userHeight", DEFAULT_AVATAR_HEIGHT).toDouble());
settings.endGroup();

View file

@ -250,7 +250,7 @@ public:
Q_ENUM(DriveKeys)
explicit MyAvatar(QThread* thread);
~MyAvatar();
virtual ~MyAvatar();
void instantiableAvatar() override {};
void registerMetaTypes(ScriptEnginePointer engine);
@ -469,16 +469,6 @@ public:
* @param {boolean} on
*/
Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; }
/**jsdoc
* @function MyAvatar.getClearOverlayWhenMoving
* @returns {boolean}
*/
Q_INVOKABLE bool getClearOverlayWhenMoving() const { return _clearOverlayWhenMoving; }
/**jsdoc
* @function MyAvatar.setClearOverlayWhenMoving
* @param {boolean} on
*/
Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; }
/**jsdoc
@ -1374,6 +1364,7 @@ private slots:
protected:
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override;
virtual void forgetChild(SpatiallyNestablePointer newChild) const override;
virtual void recalculateChildCauterization() const override;
private:
@ -1433,7 +1424,7 @@ private:
std::array<float, MAX_DRIVE_KEYS> _driveKeys;
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
bool _enableFlying { true };
bool _enableFlying { false };
bool _wasPushing { false };
bool _isPushing { false };
bool _isBeingPushed { false };
@ -1495,7 +1486,6 @@ private:
ThreadSafeValueCache<QUrl> _prefOverrideAnimGraphUrl;
QUrl _fstAnimGraphOverrideUrl;
bool _useSnapTurn { true };
bool _clearOverlayWhenMoving { true };
QString _dominantHand { DOMINANT_RIGHT_HAND };
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // degrees
@ -1566,6 +1556,7 @@ private:
glm::quat _goToOrientation;
std::unordered_set<int> _headBoneSet;
std::unordered_set<SpatiallyNestablePointer> _cauterizedChildrenOfHead;
bool _prevShouldDrawHead;
bool _rigEnabled { true };
@ -1621,7 +1612,7 @@ private:
// height of user in sensor space, when standing erect.
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
void updateChildCauterization(SpatiallyNestablePointer object);
void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize);
// max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };

View file

@ -0,0 +1,60 @@
//
// Created by Bradley Austin Davis on 2017/04/27
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OtherAvatar.h"
#include "Application.h"
OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) {
// give the pointer to our head to inherited _headData variable from AvatarData
_headData = new Head(this);
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr);
_skeletonModel->setLoadingPriority(OTHERAVATAR_LOADING_PRIORITY);
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
// add the purple orb
createOrb();
}
OtherAvatar::~OtherAvatar() {
removeOrb();
}
void OtherAvatar::removeOrb() {
if (qApp->getOverlays().isAddedOverlay(_otherAvatarOrbMeshPlaceholderID)) {
qApp->getOverlays().deleteOverlay(_otherAvatarOrbMeshPlaceholderID);
}
}
void OtherAvatar::updateOrbPosition() {
if (_otherAvatarOrbMeshPlaceholder != nullptr) {
_otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition());
}
}
void OtherAvatar::createOrb() {
if (_otherAvatarOrbMeshPlaceholderID == UNKNOWN_OVERLAY_ID ||
!qApp->getOverlays().isAddedOverlay(_otherAvatarOrbMeshPlaceholderID)) {
_otherAvatarOrbMeshPlaceholder = std::make_shared<Sphere3DOverlay>();
_otherAvatarOrbMeshPlaceholder->setAlpha(1.0f);
_otherAvatarOrbMeshPlaceholder->setColor({ 0xFF, 0x00, 0xFF });
_otherAvatarOrbMeshPlaceholder->setIsSolid(false);
_otherAvatarOrbMeshPlaceholder->setPulseMin(0.5);
_otherAvatarOrbMeshPlaceholder->setPulseMax(1.0);
_otherAvatarOrbMeshPlaceholder->setColorPulse(1.0);
_otherAvatarOrbMeshPlaceholder->setIgnoreRayIntersection(true);
_otherAvatarOrbMeshPlaceholder->setDrawInFront(false);
_otherAvatarOrbMeshPlaceholderID = qApp->getOverlays().addOverlay(_otherAvatarOrbMeshPlaceholder);
// Position focus
_otherAvatarOrbMeshPlaceholder->setWorldOrientation(glm::quat(0.0f, 0.0f, 0.0f, 1.0));
_otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition());
_otherAvatarOrbMeshPlaceholder->setDimensions(glm::vec3(0.5f, 0.5f, 0.5f));
_otherAvatarOrbMeshPlaceholder->setVisible(true);
}
}

View file

@ -0,0 +1,32 @@
//
// Created by Bradley Austin Davis on 2017/04/27
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_OtherAvatar_h
#define hifi_OtherAvatar_h
#include <avatars-renderer/Avatar.h>
#include "ui/overlays/Overlays.h"
#include "ui/overlays/Sphere3DOverlay.h"
#include "InterfaceLogging.h"
class OtherAvatar : public Avatar {
public:
explicit OtherAvatar(QThread* thread);
virtual ~OtherAvatar();
virtual void instantiableAvatar() override { };
virtual void createOrb() override;
void updateOrbPosition();
void removeOrb();
protected:
std::shared_ptr<Sphere3DOverlay> _otherAvatarOrbMeshPlaceholder { nullptr };
OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID };
};
#endif // hifi_OtherAvatar_h

View file

@ -28,15 +28,15 @@
// account synthesizes a result {status: 'success', data: {keyStatus: "preexisting"|"conflicting"|"ok"}}
QJsonObject Ledger::apiResponse(const QString& label, QNetworkReply& reply) {
QByteArray response = reply.readAll();
QJsonObject Ledger::apiResponse(const QString& label, QNetworkReply* reply) {
QByteArray response = reply->readAll();
QJsonObject data = QJsonDocument::fromJson(response).object();
qInfo(commerce) << label << "response" << QJsonDocument(data).toJson(QJsonDocument::Compact);
return data;
}
// Non-200 responses are not json:
QJsonObject Ledger::failResponse(const QString& label, QNetworkReply& reply) {
QString response = reply.readAll();
QJsonObject Ledger::failResponse(const QString& label, QNetworkReply* reply) {
QString response = reply->readAll();
qWarning(commerce) << "FAILED" << label << response;
// tempResult will be NULL if the response isn't valid JSON.
@ -52,8 +52,8 @@ QJsonObject Ledger::failResponse(const QString& label, QNetworkReply& reply) {
return tempResult.object();
}
}
#define ApiHandler(NAME) void Ledger::NAME##Success(QNetworkReply& reply) { emit NAME##Result(apiResponse(#NAME, reply)); }
#define FailHandler(NAME) void Ledger::NAME##Failure(QNetworkReply& reply) { emit NAME##Result(failResponse(#NAME, reply)); }
#define ApiHandler(NAME) void Ledger::NAME##Success(QNetworkReply* reply) { emit NAME##Result(apiResponse(#NAME, reply)); }
#define FailHandler(NAME) void Ledger::NAME##Failure(QNetworkReply* reply) { emit NAME##Result(failResponse(#NAME, reply)); }
#define Handler(NAME) ApiHandler(NAME) FailHandler(NAME)
Handler(buy)
Handler(receiveAt)
@ -68,7 +68,7 @@ Handler(updateItem)
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) {
auto accountManager = DependencyManager::get<AccountManager>();
const QString URL = "/api/v1/commerce/";
JSONCallbackParameters callbackParams(this, success, this, fail);
JSONCallbackParameters callbackParams(this, success, fail);
qCInfo(commerce) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact);
accountManager->sendRequest(URL + endpoint,
authType,
@ -223,12 +223,12 @@ QString transactionString(const QJsonObject& valueObject) {
}
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
void Ledger::historySuccess(QNetworkReply& reply) {
void Ledger::historySuccess(QNetworkReply* reply) {
// here we send a historyResult with some extra stuff in it
// Namely, the styled text we'd like to show. The issue is the
// QML cannot do that easily since it doesn't know what the wallet
// public key(s) are. Let's keep it that way
QByteArray response = reply.readAll();
QByteArray response = reply->readAll();
QJsonObject data = QJsonDocument::fromJson(response).object();
qInfo(commerce) << "history" << "response" << QJsonDocument(data).toJson(QJsonDocument::Compact);
@ -262,7 +262,7 @@ void Ledger::historySuccess(QNetworkReply& reply) {
emit historyResult(newData);
}
void Ledger::historyFailure(QNetworkReply& reply) {
void Ledger::historyFailure(QNetworkReply* reply) {
failResponse("history", reply);
}
@ -273,10 +273,10 @@ void Ledger::history(const QStringList& keys, const int& pageNumber, const int&
keysQuery("history", "historySuccess", "historyFailure", params);
}
void Ledger::accountSuccess(QNetworkReply& reply) {
void Ledger::accountSuccess(QNetworkReply* reply) {
// lets set the appropriate stuff in the wallet now
auto wallet = DependencyManager::get<Wallet>();
QByteArray response = reply.readAll();
QByteArray response = reply->readAll();
QJsonObject data = QJsonDocument::fromJson(response).object()["data"].toObject();
auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8());
@ -312,7 +312,7 @@ void Ledger::accountSuccess(QNetworkReply& reply) {
emit accountResult(responseData);
}
void Ledger::accountFailure(QNetworkReply& reply) {
void Ledger::accountFailure(QNetworkReply* reply) {
failResponse("account", reply);
}
void Ledger::account() {
@ -320,8 +320,8 @@ void Ledger::account() {
}
// The api/failResponse is called just for the side effect of logging.
void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLocation", reply); }
void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); }
void Ledger::updateLocationSuccess(QNetworkReply* reply) { apiResponse("updateLocation", reply); }
void Ledger::updateLocationFailure(QNetworkReply* reply) { failResponse("updateLocation", reply); }
void Ledger::updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings, const bool controlledFailure) {
auto wallet = DependencyManager::get<Wallet>();
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
@ -349,11 +349,11 @@ void Ledger::updateLocation(const QString& asset_id, const QString& location, co
}
}
void Ledger::certificateInfoSuccess(QNetworkReply& reply) {
void Ledger::certificateInfoSuccess(QNetworkReply* reply) {
auto wallet = DependencyManager::get<Wallet>();
auto accountManager = DependencyManager::get<AccountManager>();
QByteArray response = reply.readAll();
QByteArray response = reply->readAll();
QJsonObject replyObject = QJsonDocument::fromJson(response).object();
QStringList keys = wallet->listPublicKeys();
@ -366,7 +366,7 @@ void Ledger::certificateInfoSuccess(QNetworkReply& reply) {
qInfo(commerce) << "certificateInfo" << "response" << QJsonDocument(replyObject).toJson(QJsonDocument::Compact);
emit certificateInfoResult(replyObject);
}
void Ledger::certificateInfoFailure(QNetworkReply& reply) {
void Ledger::certificateInfoFailure(QNetworkReply* reply) {
emit certificateInfoResult(failResponse("certificateInfo", reply));
}
void Ledger::certificateInfo(const QString& certificateId) {

View file

@ -65,36 +65,36 @@ signals:
void updateCertificateStatus(const QString& certID, uint certStatus);
public slots:
void buySuccess(QNetworkReply& reply);
void buyFailure(QNetworkReply& reply);
void receiveAtSuccess(QNetworkReply& reply);
void receiveAtFailure(QNetworkReply& reply);
void balanceSuccess(QNetworkReply& reply);
void balanceFailure(QNetworkReply& reply);
void inventorySuccess(QNetworkReply& reply);
void inventoryFailure(QNetworkReply& reply);
void historySuccess(QNetworkReply& reply);
void historyFailure(QNetworkReply& reply);
void accountSuccess(QNetworkReply& reply);
void accountFailure(QNetworkReply& reply);
void updateLocationSuccess(QNetworkReply& reply);
void updateLocationFailure(QNetworkReply& reply);
void certificateInfoSuccess(QNetworkReply& reply);
void certificateInfoFailure(QNetworkReply& reply);
void transferAssetToNodeSuccess(QNetworkReply& reply);
void transferAssetToNodeFailure(QNetworkReply& reply);
void transferAssetToUsernameSuccess(QNetworkReply& reply);
void transferAssetToUsernameFailure(QNetworkReply& reply);
void alreadyOwnedSuccess(QNetworkReply& reply);
void alreadyOwnedFailure(QNetworkReply& reply);
void availableUpdatesSuccess(QNetworkReply& reply);
void availableUpdatesFailure(QNetworkReply& reply);
void updateItemSuccess(QNetworkReply& reply);
void updateItemFailure(QNetworkReply& reply);
void buySuccess(QNetworkReply* reply);
void buyFailure(QNetworkReply* reply);
void receiveAtSuccess(QNetworkReply* reply);
void receiveAtFailure(QNetworkReply* reply);
void balanceSuccess(QNetworkReply* reply);
void balanceFailure(QNetworkReply* reply);
void inventorySuccess(QNetworkReply* reply);
void inventoryFailure(QNetworkReply* reply);
void historySuccess(QNetworkReply* reply);
void historyFailure(QNetworkReply* reply);
void accountSuccess(QNetworkReply* reply);
void accountFailure(QNetworkReply* reply);
void updateLocationSuccess(QNetworkReply* reply);
void updateLocationFailure(QNetworkReply* reply);
void certificateInfoSuccess(QNetworkReply* reply);
void certificateInfoFailure(QNetworkReply* reply);
void transferAssetToNodeSuccess(QNetworkReply* reply);
void transferAssetToNodeFailure(QNetworkReply* reply);
void transferAssetToUsernameSuccess(QNetworkReply* reply);
void transferAssetToUsernameFailure(QNetworkReply* reply);
void alreadyOwnedSuccess(QNetworkReply* reply);
void alreadyOwnedFailure(QNetworkReply* reply);
void availableUpdatesSuccess(QNetworkReply* reply);
void availableUpdatesFailure(QNetworkReply* reply);
void updateItemSuccess(QNetworkReply* reply);
void updateItemFailure(QNetworkReply* reply);
private:
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);
QJsonObject failResponse(const QString& label, QNetworkReply& reply);
QJsonObject apiResponse(const QString& label, QNetworkReply* reply);
QJsonObject failResponse(const QString& label, QNetworkReply* reply);
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& extraRequestParams);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail);

View file

@ -127,31 +127,40 @@ EC_KEY* readKeys(const char* filename) {
bool Wallet::writeBackupInstructions() {
QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html");
QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE);
QFile inputFile(inputFilename);
QFile outputFile(outputFilename);
bool retval = false;
if (QFile::exists(outputFilename) || getKeyFilePath() == "")
if (getKeyFilePath() == "")
{
return false;
}
QFile::copy(inputFilename, outputFilename);
if (QFile::exists(outputFilename) && outputFile.open(QIODevice::ReadWrite)) {
if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) {
if (outputFile.open(QIODevice::ReadWrite)) {
// Read the data from the original file, then close it
QByteArray fileData = inputFile.readAll();
inputFile.close();
QByteArray fileData = outputFile.readAll();
QString text(fileData);
// Translate the data from the original file into a QString
QString text(fileData);
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
// Replace the necessary string
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
outputFile.seek(0); // go to the beginning of the file
outputFile.write(text.toUtf8()); // write the new text back to the file
// Write the new text back to the file
outputFile.write(text.toUtf8());
outputFile.close(); // close the file handle.
// Close the output file
outputFile.close();
retval = true;
qCDebug(commerce) << "wrote html file successfully";
retval = true;
qCDebug(commerce) << "wrote html file successfully";
} else {
qCDebug(commerce) << "failed to open output html file" << outputFilename;
}
} else {
qCDebug(commerce) << "failed to open output html file" << outputFilename;
qCDebug(commerce) << "failed to open input html file" << inputFilename;
}
return retval;
}

View file

@ -35,5 +35,8 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting, const QVar
}
void SettingsScriptingInterface::setValue(const QString& setting, const QVariant& value) {
Setting::Handle<QVariant>(setting).set(value);
// Make a deep-copy of the string.
// Dangling pointers can occur with QStrings that are implicitly shared from a QScriptEngine.
QString deepCopy = QString::fromUtf16(setting.utf16());
Setting::Handle<QVariant>(deepCopy).set(value);
}

View file

@ -113,9 +113,8 @@ void LoginDialog::linkSteam() {
}
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "linkCompleted";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "linkFailed";
const QString LINK_STEAM_PATH = "api/v1/user/steam/link";
@ -141,9 +140,8 @@ void LoginDialog::createAccountFromStream(QString username) {
}
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "createCompleted";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "createFailed";
const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create";
@ -185,28 +183,27 @@ void LoginDialog::openUrl(const QString& url) const {
}
}
void LoginDialog::linkCompleted(QNetworkReply& reply) {
void LoginDialog::linkCompleted(QNetworkReply* reply) {
emit handleLinkCompleted();
}
void LoginDialog::linkFailed(QNetworkReply& reply) {
emit handleLinkFailed(reply.errorString());
void LoginDialog::linkFailed(QNetworkReply* reply) {
emit handleLinkFailed(reply->errorString());
}
void LoginDialog::createCompleted(QNetworkReply& reply) {
void LoginDialog::createCompleted(QNetworkReply* reply) {
emit handleCreateCompleted();
}
void LoginDialog::createFailed(QNetworkReply& reply) {
emit handleCreateFailed(reply.errorString());
void LoginDialog::createFailed(QNetworkReply* reply) {
emit handleCreateFailed(reply->errorString());
}
void LoginDialog::signup(const QString& email, const QString& username, const QString& password) {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "signupCompleted";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "signupFailed";
QJsonObject payload;
@ -228,7 +225,7 @@ void LoginDialog::signup(const QString& email, const QString& username, const QS
QJsonDocument(payload).toJson());
}
void LoginDialog::signupCompleted(QNetworkReply& reply) {
void LoginDialog::signupCompleted(QNetworkReply* reply) {
emit handleSignupCompleted();
}
@ -242,10 +239,10 @@ QString errorStringFromAPIObject(const QJsonValue& apiObject) {
}
}
void LoginDialog::signupFailed(QNetworkReply& reply) {
void LoginDialog::signupFailed(QNetworkReply* reply) {
// parse the returned JSON to see what the problem was
auto jsonResponse = QJsonDocument::fromJson(reply.readAll());
auto jsonResponse = QJsonDocument::fromJson(reply->readAll());
static const QString RESPONSE_DATA_KEY = "data";

View file

@ -42,14 +42,14 @@ signals:
void handleSignupFailed(QString errorString);
public slots:
void linkCompleted(QNetworkReply& reply);
void linkFailed(QNetworkReply& reply);
void linkCompleted(QNetworkReply* reply);
void linkFailed(QNetworkReply* reply);
void createCompleted(QNetworkReply& reply);
void createFailed(QNetworkReply& reply);
void createCompleted(QNetworkReply* reply);
void createFailed(QNetworkReply* reply);
void signupCompleted(QNetworkReply& reply);
void signupFailed(QNetworkReply& reply);
void signupCompleted(QNetworkReply* reply);
void signupFailed(QNetworkReply* reply);
protected slots:
Q_INVOKABLE bool isSteamRunning() const;

View file

@ -88,38 +88,24 @@ void OverlayConductor::update(float dt) {
_hmdMode = false;
}
bool isAtRest = updateAvatarIsAtRest();
bool isMoving = !isAtRest;
bool shouldRecenter = false;
if (_flags & SuppressedByMove) {
if (!isMoving) {
_flags &= ~SuppressedByMove;
shouldRecenter = true;
}
} else {
if (myAvatar->getClearOverlayWhenMoving() && isMoving) {
_flags |= SuppressedByMove;
}
}
if (_flags & SuppressedByHead) {
if (isAtRest) {
_flags &= ~SuppressedByHead;
if (_suppressedByHead) {
if (updateAvatarIsAtRest()) {
_suppressedByHead = false;
shouldRecenter = true;
}
} else {
if (_hmdMode && headOutsideOverlay()) {
_flags |= SuppressedByHead;
_suppressedByHead = true;
}
}
bool targetVisible = Menu::getInstance()->isOptionChecked(MenuOption::Overlays) && (0 == (_flags & SuppressMask));
bool targetVisible = Menu::getInstance()->isOptionChecked(MenuOption::Overlays) && !_suppressedByHead;
if (targetVisible != currentVisible) {
offscreenUi->setPinned(!targetVisible);
}
if (shouldRecenter && !_flags) {
if (shouldRecenter && !_suppressedByHead) {
centerUI();
}
}

View file

@ -25,13 +25,7 @@ private:
bool headOutsideOverlay() const;
bool updateAvatarIsAtRest();
enum SupressionFlags {
SuppressedByMove = 0x01,
SuppressedByHead = 0x02,
SuppressMask = 0x03,
};
uint8_t _flags { SuppressedByMove };
bool _suppressedByHead { false };
bool _hmdMode { false };
// used by updateAvatarIsAtRest

View file

@ -161,12 +161,6 @@ void setupPreferences() {
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use reticle cursor instead of arrow", getter, setter));
}
{
auto getter = [=]()->bool { return myAvatar->getClearOverlayWhenMoving(); };
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Clear overlays when moving", getter, setter));
}
static const QString VIEW_CATEGORY{ "View" };
{
auto getter = [=]()->float { return myAvatar->getRealWorldFieldOfView(); };

View file

@ -493,7 +493,7 @@ void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) {
multiPart->append(imagePart);
auto accountManager = DependencyManager::get<AccountManager>();
JSONCallbackParameters callbackParams(uploader, "uploadSuccess", uploader, "uploadFailure");
JSONCallbackParameters callbackParams(uploader, "uploadSuccess", "uploadFailure");
accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, AccountManagerAuth::Required, QNetworkAccessManager::PostOperation,
callbackParams, nullptr, multiPart);

View file

@ -23,11 +23,11 @@ SnapshotUploader::SnapshotUploader(QUrl inWorldLocation, QString pathname) :
_pathname(pathname) {
}
void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
void SnapshotUploader::uploadSuccess(QNetworkReply* reply) {
const QString STORY_UPLOAD_URL = "/api/v1/user_stories";
// parse the reply for the thumbnail_url
QByteArray contents = reply.readAll();
QByteArray contents = reply->readAll();
QJsonParseError jsonError;
auto doc = QJsonDocument::fromJson(contents, &jsonError);
if (jsonError.error == QJsonParseError::NoError) {
@ -60,7 +60,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
rootObject.insert("user_story", userStoryObject);
auto accountManager = DependencyManager::get<AccountManager>();
JSONCallbackParameters callbackParams(this, "createStorySuccess", this, "createStoryFailure");
JSONCallbackParameters callbackParams(this, "createStorySuccess", "createStoryFailure");
accountManager->sendRequest(STORY_UPLOAD_URL,
AccountManagerAuth::Required,
@ -74,11 +74,11 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
}
}
void SnapshotUploader::uploadFailure(QNetworkReply& reply) {
QString replyString = reply.readAll();
qDebug() << "Error " << reply.errorString() << " uploading snapshot " << _pathname << " from " << _inWorldLocation;
void SnapshotUploader::uploadFailure(QNetworkReply* reply) {
QString replyString = reply->readAll();
qDebug() << "Error " << reply->errorString() << " uploading snapshot " << _pathname << " from " << _inWorldLocation;
if (replyString.size() == 0) {
replyString = reply.errorString();
replyString = reply->errorString();
}
replyString = replyString.left(1000); // Only print first 1000 characters of error
qDebug() << "Snapshot upload reply error (truncated):" << replyString;
@ -86,17 +86,17 @@ void SnapshotUploader::uploadFailure(QNetworkReply& reply) {
this->deleteLater();
}
void SnapshotUploader::createStorySuccess(QNetworkReply& reply) {
QString replyString = reply.readAll();
void SnapshotUploader::createStorySuccess(QNetworkReply* reply) {
QString replyString = reply->readAll();
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(false, replyString);
this->deleteLater();
}
void SnapshotUploader::createStoryFailure(QNetworkReply& reply) {
QString replyString = reply.readAll();
qDebug() << "Error " << reply.errorString() << " uploading snapshot story " << _pathname << " from " << _inWorldLocation;
void SnapshotUploader::createStoryFailure(QNetworkReply* reply) {
QString replyString = reply->readAll();
qDebug() << "Error " << reply->errorString() << " uploading snapshot story " << _pathname << " from " << _inWorldLocation;
if (replyString.size() == 0) {
replyString = reply.errorString();
replyString = reply->errorString();
}
replyString = replyString.left(1000); // Only print first 1000 characters of error
qDebug() << "Snapshot story upload reply error (truncated):" << replyString;

View file

@ -21,12 +21,12 @@ class SnapshotUploader : public QObject {
public:
SnapshotUploader(QUrl inWorldLocation, QString pathname);
public slots:
void uploadSuccess(QNetworkReply& reply);
void uploadFailure(QNetworkReply& reply);
void createStorySuccess(QNetworkReply& reply);
void createStoryFailure(QNetworkReply& reply);
void uploadSuccess(QNetworkReply* reply);
void uploadFailure(QNetworkReply* reply);
void createStorySuccess(QNetworkReply* reply);
void createStoryFailure(QNetworkReply* reply);
private:
QUrl _inWorldLocation;
QString _pathname;
};
#endif // hifi_SnapshotUploader_h
#endif // hifi_SnapshotUploader_h

View file

@ -1426,6 +1426,8 @@ bool AudioClient::setIsStereoInput(bool isStereoInput) {
// restart the input device
switchInputToAudioDevice(_inputDeviceInfo);
emit isStereoInputChanged(_isStereoInput);
}
return stereoInputChanged;
@ -1463,6 +1465,8 @@ void AudioClient::outputFormatChanged() {
}
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) {
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]";
bool supportedFormat = false;
@ -1663,6 +1667,8 @@ void AudioClient::outputNotify() {
}
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) {
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
bool supportedFormat = false;
@ -2021,7 +2027,7 @@ void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 sca
void AudioClient::startThread() {
moveToNewNamedThread(this, "Audio Thread", [this] { start(); });
moveToNewNamedThread(this, "Audio Thread", [this] { start(); }, QThread::TimeCriticalPriority);
}
void AudioClient::setInputVolume(float volume, bool emitSignal) {

View file

@ -44,6 +44,9 @@ public slots:
virtual bool setIsStereoInput(bool stereo) = 0;
virtual bool isStereoInput() = 0;
signals:
void isStereoInputChanged(bool isStereo);
};
Q_DECLARE_METATYPE(AbstractAudioInterface*)

View file

@ -1338,6 +1338,9 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const {
}
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
if (!isMyAvatar()) {
createOrb();
}
AvatarData::setSkeletonModelURL(skeletonModelURL);
if (QThread::currentThread() == thread()) {
_skeletonModel->setURL(_skeletonModelURL);

View file

@ -23,7 +23,6 @@
#include <graphics-scripting/Forward.h>
#include <GLMHelpers.h>
#include "Head.h"
#include "SkeletonModel.h"
#include "Rig.h"
@ -41,7 +40,6 @@ static const float SCALING_RATIO = .05f;
extern const float CHAT_MESSAGE_SCALE;
extern const float CHAT_MESSAGE_HEIGHT;
enum ScreenTintLayer {
SCREEN_TINT_BEFORE_LANDSCAPE = 0,
SCREEN_TINT_BEFORE_AVATARS,
@ -69,7 +67,7 @@ public:
static void setShowNamesAboveHeads(bool show);
explicit Avatar(QThread* thread);
~Avatar();
virtual ~Avatar();
virtual void instantiableAvatar() = 0;
@ -109,6 +107,7 @@ public:
float getLODDistance() const;
virtual bool isMyAvatar() const override { return false; }
virtual void createOrb() { }
virtual QVector<glm::quat> getJointRotations() const override;
using AvatarData::getJointRotation;
@ -167,8 +166,8 @@ public:
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
static void renderJointConnectingCone( gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2,
float radius1, float radius2, const glm::vec4& color);
static void renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2,
float radius1, float radius2, const glm::vec4& color);
virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { }
@ -235,7 +234,7 @@ public:
/// Scales a world space position vector relative to the avatar position and scale
/// \param vector position to be scaled. Will store the result
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
void scaleVectorRelativeToPosition(glm::vec3& positionToScale) const;
void slamPosition(const glm::vec3& position);
virtual void updateAttitude(const glm::quat& orientation) override;
@ -254,7 +253,6 @@ public:
void setPositionViaScript(const glm::vec3& position) override;
void setOrientationViaScript(const glm::quat& orientation) override;
/**jsdoc
* @function MyAvatar.getParentID
* @returns {Uuid}
@ -283,7 +281,6 @@ public:
// This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript.
Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex) override;
/**jsdoc
* Returns an array of joints, where each joint is an object containing name, index, and parentIndex fields.
* @function MyAvatar.getSkeleton
@ -349,7 +346,6 @@ public:
// not all subclasses of AvatarData have access to this data.
virtual bool canMeasureEyeHeight() const override { return true; }
virtual float getModelScale() const { return _modelScale; }
virtual void setModelScale(float scale) { _modelScale = scale; }
virtual glm::vec3 scaleForChildren() const override { return glm::vec3(getModelScale()); }

View file

@ -1,19 +0,0 @@
//
// Created by Bradley Austin Davis on 2017/04/27
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OtherAvatar.h"
OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) {
// give the pointer to our head to inherited _headData variable from AvatarData
_headData = new Head(this);
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr);
_skeletonModel->setLoadingPriority(OTHERAVATAR_LOADING_PRIORITY);
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
}

View file

@ -1,20 +0,0 @@
//
// Created by Bradley Austin Davis on 2017/04/27
// Copyright 2013-2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_OtherAvatar_h
#define hifi_OtherAvatar_h
#include "Avatar.h"
class OtherAvatar : public Avatar {
public:
explicit OtherAvatar(QThread* thread);
virtual void instantiableAvatar() override {};
};
#endif // hifi_OtherAvatar_h

View file

@ -363,6 +363,14 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
return false;
}
void EntityRenderer::updateModelTransform() {
bool success = false;
auto newModelTransform = _entity->getTransformToCenter(success);
if (success) {
_modelTransform = newModelTransform;
}
}
void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) {
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
withWriteLock([&] {
@ -419,4 +427,4 @@ void EntityRenderer::addMaterial(graphics::MaterialLayer material, const std::st
void EntityRenderer::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[parentMaterialName].remove(material);
}
}

View file

@ -97,6 +97,7 @@ protected:
virtual void doRender(RenderArgs* args) = 0;
bool isFading() const { return _isFading; }
void updateModelTransform();
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
@ -140,6 +141,7 @@ protected:
bool _needsRenderUpdate { false };
// Only touched on the rendering thread
bool _renderUpdateQueued{ false };
Transform _renderTransform;
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;

View file

@ -35,7 +35,6 @@ private:
glm::vec2 _materialMappingPos;
glm::vec2 _materialMappingScale;
float _materialMappingRot;
Transform _renderTransform;
std::shared_ptr<NetworkMaterial> _drawMaterial;
};

View file

@ -279,18 +279,16 @@ EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlag
}
bool RenderableModelEntityItem::supportsDetailedRayIntersection() const {
return isModelLoaded();
return true;
}
bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance, BoxFace& face,
glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const {
auto model = getModel();
if (!model) {
return true;
if (!model || !isModelLoaded()) {
return false;
}
// qCDebug(entitiesrenderer) << "RenderableModelEntityItem::findDetailedRayIntersection() precisionPicking:"
// << precisionPicking;
return model->findRayIntersectionAgainstSubMeshes(origin, direction, distance,
face, surfaceNormal, extraInfo, precisionPicking, false);

View file

@ -122,6 +122,14 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
});
}
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
withWriteLock([&] {
updateModelTransform();
_renderTransform = getModelTransform();
});
});
}
void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
@ -319,7 +327,10 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
// In trail mode, the particles are created in world space.
// so we only set a transform if they're not in trail mode
if (!_particleProperties.emission.shouldTrail) {
transform = getModelTransform();
withReadLock([&] {
transform = _renderTransform;
});
transform.setScale(vec3(1));
}
batch.setModelTransform(transform);

View file

@ -97,16 +97,25 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
addMaterial(graphics::MaterialLayer(_material, 0), "0");
_shape = entity->getShape();
_position = entity->getWorldPosition();
_dimensions = entity->getScaledDimensions();
_orientation = entity->getWorldOrientation();
_renderTransform = getModelTransform();
});
if (_shape == entity::Sphere) {
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
withWriteLock([&] {
auto entity = getEntity();
_position = entity->getWorldPosition();
_dimensions = entity->getScaledDimensions();
_orientation = entity->getWorldOrientation();
bool success = false;
auto newModelTransform = entity->getTransformToCenter(success);
_renderTransform = success ? newModelTransform : getModelTransform();
_renderTransform.postScale(_dimensions);
if (_shape == entity::Sphere) {
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
}
_renderTransform.postScale(_dimensions);
});;
});
}

View file

@ -40,7 +40,6 @@ private:
Procedural _procedural;
QString _lastUserData;
Transform _renderTransform;
entity::Shape _shape { entity::Sphere };
std::shared_ptr<graphics::Material> _material;
glm::vec3 _position;

View file

@ -20,6 +20,7 @@
#include "GLMHelpers.h"
using namespace render;
using namespace render::entities;
static const int FIXED_FONT_POINT_SIZE = 40;
@ -64,10 +65,20 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return false;
}
void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
withWriteLock([&] {
_dimensions = entity->getScaledDimensions();
updateModelTransform();
_renderTransform = getModelTransform();
});
});
}
void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
_textColor = toGlm(entity->getTextColorX());
_backgroundColor = toGlm(entity->getBackgroundColorX());
_dimensions = entity->getScaledDimensions();
_faceCamera = entity->getFaceCamera();
_lineHeight = entity->getLineHeight();
_text = entity->getText();
@ -76,24 +87,28 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
void TextEntityRenderer::doRender(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
Transform modelTransform;
glm::vec3 dimensions;
withReadLock([&] {
modelTransform = _renderTransform;
dimensions = _dimensions;
});
static const float SLIGHTLY_BEHIND = -0.005f;
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
bool transparent = fadeRatio < 1.0f;
glm::vec4 textColor = glm::vec4(_textColor, fadeRatio);
glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio);
const glm::vec3& dimensions = _dimensions;
// Render background
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
const auto& modelTransform = getModelTransform();
auto transformToTopLeft = modelTransform;
if (_faceCamera) {
//rotate about vertical to face the camera
@ -105,7 +120,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
}
transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
batch.setModelTransform(transformToTopLeft);
auto geometryCache = DependencyManager::get<GeometryCache>();
if (!_geometryID) {
@ -113,11 +128,11 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
}
geometryCache->bindSimpleProgram(batch, false, transparent, false, false, false);
geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID);
float scale = _lineHeight / _textRenderer->getFontSize();
transformToTopLeft.setScale(scale); // Scale to have the correct line height
batch.setModelTransform(transformToTopLeft);
float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight;
glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin,
dimensions.y - 2.0f * topMargin);

View file

@ -27,6 +27,7 @@ public:
~TextEntityRenderer();
private:
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
int _geometryID{ 0 };
@ -39,6 +40,6 @@ private:
float _lineHeight;
};
} }
} }
#endif // hifi_RenderableTextEntityItem_h

View file

@ -149,8 +149,8 @@ void WebEntityRenderer::onTimeout() {
}
void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
// If the content type has changed, or the old content type was QML, we need to
// destroy the existing surface (because surfaces don't support changing the root
// If the content type has changed, or the old content type was QML, we need to
// destroy the existing surface (because surfaces don't support changing the root
// object, so subsequent loads of content just overlap the existing content
bool urlChanged = false;
{
@ -187,24 +187,30 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
if (!hasWebSurface() && !buildWebSurface(entity)) {
return;
}
if (urlChanged && _contentType == ContentType::HtmlContent) {
_webSurface->getRootItem()->setProperty(URL_PROPERTY, _lastSourceUrl);
}
if (_contextPosition != entity->getWorldPosition()) {
// update globalPosition
_contextPosition = entity->getWorldPosition();
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
withWriteLock([&] {
if (_contextPosition != entity->getWorldPosition()) {
// update globalPosition
_contextPosition = entity->getWorldPosition();
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
}
_lastDPI = entity->getDPI();
_lastLocked = entity->getLocked();
_lastDPI = entity->getDPI();
_lastLocked = entity->getLocked();
glm::vec2 windowSize = getWindowSize(entity);
_webSurface->resize(QSize(windowSize.x, windowSize.y));
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getScaledDimensions());
glm::vec2 windowSize = getWindowSize(entity);
_webSurface->resize(QSize(windowSize.x, windowSize.y));
updateModelTransform();
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getScaledDimensions());
});
});
});
}
@ -297,7 +303,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
if (_contentType == ContentType::HtmlContent) {
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the
// web entity
if (QUrl(_lastSourceUrl).host().endsWith("youtube.com", Qt::CaseInsensitive)) {
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);

View file

@ -68,7 +68,6 @@ private:
bool _lastLocked;
QTimer _timer;
uint64_t _lastRenderTime { 0 };
Transform _renderTransform;
};
} } // namespace

View file

@ -123,7 +123,6 @@ private:
bool _pendingSkyboxTexture{ false };
QString _proceduralUserData;
Transform _renderTransform;
};
} } // namespace

View file

@ -185,6 +185,7 @@ public:
/// Dimensions in meters (0.0 - TREE_SCALE)
glm::vec3 getScaledDimensions() const;
virtual void setScaledDimensions(const glm::vec3& value);
virtual glm::vec3 getRaycastDimensions() const { return getScaledDimensions(); }
inline const glm::vec3 getUnscaledDimensions() const { return _unscaledDimensions; }
virtual void setUnscaledDimensions(const glm::vec3& value);
@ -239,7 +240,7 @@ public:
// position, size, and bounds related helpers
virtual AACube getMaximumAACube(bool& success) const override;
AACube getMinimumAACube(bool& success) const;
AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters)
virtual AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters)
using SpatiallyNestable::getQueryAACube;
virtual AACube getQueryAACube(bool& success) const override;

View file

@ -214,7 +214,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 dimensions = entity->getScaledDimensions();
glm::vec3 dimensions = entity->getRaycastDimensions();
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
@ -312,7 +312,7 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
glm::vec3 penetration;
if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
glm::vec3 dimensions = entity->getScaledDimensions();
glm::vec3 dimensions = entity->getRaycastDimensions();
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably dull actuall hull testing if they wanted to

View file

@ -1816,7 +1816,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
}
}
float clusterScale = extractUniformScale(fbxCluster.inverseBindMatrix);
glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform;
ShapeVertices& points = shapeVertices.at(jointIndex);
@ -1832,7 +1831,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
if (weight >= EXPANSION_WEIGHT_THRESHOLD) {
// transform to joint-frame and save for later
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(newIndex));
points.push_back(extractTranslation(vertexTransform) * clusterScale);
points.push_back(extractTranslation(vertexTransform));
}
// look for an unused slot in the weights vector
@ -1886,12 +1885,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
FBXJoint& joint = geometry.joints[jointIndex];
// transform cluster vertices to joint-frame and save for later
float clusterScale = extractUniformScale(firstFBXCluster.inverseBindMatrix);
glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform;
ShapeVertices& points = shapeVertices.at(jointIndex);
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertex);
points.push_back(extractTranslation(vertexTransform) * clusterScale);
points.push_back(extractTranslation(vertexTransform));
}
// Apply geometric offset, if present, by transforming the vertices directly

View file

@ -278,6 +278,7 @@ protected:
struct InputStageState {
bool _invalidFormat { true };
bool _lastUpdateStereoState{ false };
bool _hadColorAttribute{ true };
Stream::FormatPointer _format;
std::string _formatKey;

View file

@ -156,6 +156,14 @@ void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) {
}
void GLBackend::updateInput() {
bool isStereoNow = isStereo();
// track stereo state change potentially happening wihtout changing the input format
// this is a rare case requesting to invalid the format
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
_input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);
#endif
_input._lastUpdateStereoState = isStereoNow;
if (_input._invalidFormat) {
InputStageState::ActivationCache newActivation;
@ -213,7 +221,7 @@ void GLBackend::updateInput() {
(void)CHECK_GL_ERROR();
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereo() ? 2 : 1));
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereoNow ? 2 : 1));
#else
glVertexBindingDivisor(bufferChannelNum, frequency);
#endif

View file

@ -25,6 +25,14 @@ void GL41Backend::resetInputStage() {
}
void GL41Backend::updateInput() {
bool isStereoNow = isStereo();
// track stereo state change potentially happening wihtout changing the input format
// this is a rare case requesting to invalid the format
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
_input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);
#endif
_input._lastUpdateStereoState = isStereoNow;
if (_input._invalidFormat || _input._invalidBuffers.any()) {
if (_input._invalidFormat) {
@ -111,7 +119,7 @@ void GL41Backend::updateInput() {
reinterpret_cast<GLvoid*>(pointer + perLocationStride * (GLuint)locNum));
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1));
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereoNow ? 2 : 1));
#else
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency);
#endif

View file

@ -27,6 +27,14 @@ void GL45Backend::resetInputStage() {
}
void GL45Backend::updateInput() {
bool isStereoNow = isStereo();
// track stereo state change potentially happening wihtout changing the input format
// this is a rare case requesting to invalid the format
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
_input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);
#endif
_input._lastUpdateStereoState = isStereoNow;
if (_input._invalidFormat) {
InputStageState::ActivationCache newActivation;
@ -84,7 +92,7 @@ void GL45Backend::updateInput() {
(void)CHECK_GL_ERROR();
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereo() ? 2 : 1));
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereoNow ? 2 : 1));
#else
glVertexBindingDivisor(bufferChannelNum, frequency);
#endif

View file

@ -25,6 +25,7 @@
#include <QtCore/QThreadPool>
#include <QtNetwork/QHttpMultiPart>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <qthread.h>
#include <SettingHandle.h>
@ -46,21 +47,18 @@ Q_DECLARE_METATYPE(JSONCallbackParameters)
const QString ACCOUNTS_GROUP = "accounts";
JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
QObject* errorCallbackReceiver, const QString& errorCallbackMethod,
QObject* updateReceiver, const QString& updateSlot) :
jsonCallbackReceiver(jsonCallbackReceiver),
JSONCallbackParameters::JSONCallbackParameters(QObject* callbackReceiver,
const QString& jsonCallbackMethod,
const QString& errorCallbackMethod) :
callbackReceiver(callbackReceiver),
jsonCallbackMethod(jsonCallbackMethod),
errorCallbackReceiver(errorCallbackReceiver),
errorCallbackMethod(errorCallbackMethod),
updateReciever(updateReceiver),
updateSlot(updateSlot)
errorCallbackMethod(errorCallbackMethod)
{
}
QJsonObject AccountManager::dataObjectFromResponse(QNetworkReply &requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject AccountManager::dataObjectFromResponse(QNetworkReply* requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
static const QString STATUS_KEY = "status";
static const QString DATA_KEY = "data";
@ -74,8 +72,7 @@ QJsonObject AccountManager::dataObjectFromResponse(QNetworkReply &requestReply)
AccountManager::AccountManager(UserAgentGetter userAgentGetter) :
_userAgentGetter(userAgentGetter),
_authURL(),
_pendingCallbackMap()
_authURL()
{
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
@ -323,75 +320,66 @@ void AccountManager::sendRequest(const QString& path,
}
}
if (!callbackParams.isEmpty()) {
// if we have information for a callback, insert the callbackParams into our local map
_pendingCallbackMap.insert(networkReply, callbackParams);
if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) {
callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)),
callbackParams.updateSlot.toStdString().c_str());
connect(networkReply, &QNetworkReply::finished, this, [this, networkReply] {
// double check if the finished network reply had a session ID in the header and make
// sure that our session ID matches that value if so
if (networkReply->hasRawHeader(METAVERSE_SESSION_ID_HEADER)) {
_sessionID = networkReply->rawHeader(METAVERSE_SESSION_ID_HEADER);
}
});
if (callbackParams.isEmpty()) {
connect(networkReply, &QNetworkReply::finished, networkReply, &QNetworkReply::deleteLater);
} else {
// There's a cleaner way to fire the JSON/error callbacks below and ensure that deleteLater is called for the
// request reply - unfortunately it requires Qt 5.10 which the Android build does not support as of 06/26/18
connect(networkReply, &QNetworkReply::finished, callbackParams.callbackReceiver,
[callbackParams, networkReply] {
if (networkReply->error() == QNetworkReply::NoError) {
if (!callbackParams.jsonCallbackMethod.isEmpty()) {
bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
qPrintable(callbackParams.jsonCallbackMethod),
Q_ARG(QNetworkReply*, networkReply));
if (!invoked) {
QString error = "Could not invoke " + callbackParams.jsonCallbackMethod + " with QNetworkReply* "
+ "on callbackReceiver.";
qCWarning(networking) << error;
Q_ASSERT_X(invoked, "AccountManager::passErrorToCallback", qPrintable(error));
}
} else {
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qCDebug(networking) << "Received JSON response from metaverse API that has no matching callback.";
qCDebug(networking) << QJsonDocument::fromJson(networkReply->readAll());
}
}
} else {
if (!callbackParams.errorCallbackMethod.isEmpty()) {
bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
qPrintable(callbackParams.errorCallbackMethod),
Q_ARG(QNetworkReply*, networkReply));
if (!invoked) {
QString error = "Could not invoke " + callbackParams.errorCallbackMethod + " with QNetworkReply* "
+ "on callbackReceiver.";
qCWarning(networking) << error;
Q_ASSERT_X(invoked, "AccountManager::passErrorToCallback", qPrintable(error));
}
} else {
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qCDebug(networking) << "Received error response from metaverse API that has no matching callback.";
qCDebug(networking) << "Error" << networkReply->error() << "-" << networkReply->errorString();
qCDebug(networking) << networkReply->readAll();
}
}
}
networkReply->deleteLater();
});
}
// if we ended up firing of a request, hook up to it now
connect(networkReply, SIGNAL(finished()), SLOT(processReply()));
}
}
void AccountManager::processReply() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
if (requestReply->error() == QNetworkReply::NoError) {
if (requestReply->hasRawHeader(METAVERSE_SESSION_ID_HEADER)) {
_sessionID = requestReply->rawHeader(METAVERSE_SESSION_ID_HEADER);
}
passSuccessToCallback(requestReply);
} else {
passErrorToCallback(requestReply);
}
requestReply->deleteLater();
}
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
if (callbackParams.jsonCallbackReceiver) {
// invoke the right method on the callback receiver
QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod),
Q_ARG(QNetworkReply&, *requestReply));
// remove the related reply-callback group from the map
_pendingCallbackMap.remove(requestReply);
} else {
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qCDebug(networking) << "Received JSON response from metaverse API that has no matching callback.";
qCDebug(networking) << QJsonDocument::fromJson(requestReply->readAll());
}
requestReply->deleteLater();
}
}
void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
if (callbackParams.errorCallbackReceiver) {
// invoke the right method on the callback receiver
QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod),
Q_ARG(QNetworkReply&, *requestReply));
// remove the related reply-callback group from the map
_pendingCallbackMap.remove(requestReply);
} else {
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qCDebug(networking) << "Received error response from metaverse API that has no matching callback.";
qCDebug(networking) << "Error" << requestReply->error() << "-" << requestReply->errorString();
qCDebug(networking) << requestReply->readAll();
}
requestReply->deleteLater();
}
}
@ -817,16 +805,15 @@ void AccountManager::processGeneratedKeypair(QByteArray publicKey, QByteArray pr
// setup callback parameters so we know once the keypair upload has succeeded or failed
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
callbackParameters.callbackReceiver = this;
callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
callbackParameters.errorCallbackReceiver = this;
callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
callbackParameters, QByteArray(), requestMultiPart);
}
void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) {
void AccountManager::publicKeyUploadSucceeded(QNetworkReply* reply) {
qCDebug(networking) << "Uploaded public key to Metaverse API. RSA keypair generation is completed.";
// public key upload complete - store the matching private key and persist the account to settings
@ -838,23 +825,17 @@ void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) {
_isWaitingForKeypairResponse = false;
emit newKeypair();
// delete the reply object now that we are done with it
reply.deleteLater();
}
void AccountManager::publicKeyUploadFailed(QNetworkReply& reply) {
void AccountManager::publicKeyUploadFailed(QNetworkReply* reply) {
// the public key upload has failed
qWarning() << "Public key upload failed from AccountManager" << reply.errorString();
qWarning() << "Public key upload failed from AccountManager" << reply->errorString();
// we aren't waiting for a response any longer
_isWaitingForKeypairResponse = false;
// clear our pending private key
_pendingPrivateKey.clear();
// delete the reply object now that we are done with it
reply.deleteLater();
}
void AccountManager::handleKeypairGenerationError() {

View file

@ -28,18 +28,14 @@
class JSONCallbackParameters {
public:
JSONCallbackParameters(QObject* jsonCallbackReceiver = nullptr, const QString& jsonCallbackMethod = QString(),
QObject* errorCallbackReceiver = nullptr, const QString& errorCallbackMethod = QString(),
QObject* updateReceiver = nullptr, const QString& updateSlot = QString());
JSONCallbackParameters(QObject* callbackReceiver = nullptr, const QString& jsonCallbackMethod = QString(),
const QString& errorCallbackMethod = QString());
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
bool isEmpty() const { return !callbackReceiver; }
QObject* jsonCallbackReceiver;
QObject* callbackReceiver;
QString jsonCallbackMethod;
QObject* errorCallbackReceiver;
QString errorCallbackMethod;
QObject* updateReciever;
QString updateSlot;
};
namespace AccountManagerAuth {
@ -90,7 +86,7 @@ public:
DataServerAccountInfo& getAccountInfo() { return _accountInfo; }
void setAccountInfo(const DataServerAccountInfo &newAccountInfo);
static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply);
static QJsonObject dataObjectFromResponse(QNetworkReply* requestReply);
QUuid getSessionID() const { return _sessionID; }
void setSessionID(const QUuid& sessionID);
@ -126,11 +122,10 @@ signals:
void newKeypair();
private slots:
void processReply();
void handleKeypairGenerationError();
void processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey);
void publicKeyUploadSucceeded(QNetworkReply& reply);
void publicKeyUploadFailed(QNetworkReply& reply);
void publicKeyUploadSucceeded(QNetworkReply* reply);
void publicKeyUploadFailed(QNetworkReply* reply);
void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid());
private:
@ -146,8 +141,6 @@ private:
UserAgentGetter _userAgentGetter;
QUrl _authURL;
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
DataServerAccountInfo _accountInfo;
bool _isWaitingForTokenRefresh { false };

View file

@ -215,9 +215,8 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
static JSONCallbackParameters callbackParams;
if (!hasSetupParameters) {
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "handleAPIResponse";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "handleAPIError";
}
@ -377,8 +376,8 @@ void AddressManager::handleLookupString(const QString& lookupString, bool fromSu
const QString DATA_OBJECT_DOMAIN_KEY = "domain";
void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
void AddressManager::handleAPIResponse(QNetworkReply* requestReply) {
QJsonObject responseObject = QJsonDocument::fromJson(requestReply->readAll()).object();
QJsonObject dataObject = responseObject["data"].toObject();
// Lookup succeeded, don't keep re-trying it (especially on server restarts)
@ -396,7 +395,7 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
const char OVERRIDE_PATH_KEY[] = "override_path";
const char LOOKUP_TRIGGER_KEY[] = "lookup_trigger";
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) {
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply* reply) {
const QString DATA_OBJECT_PLACE_KEY = "place";
const QString DATA_OBJECT_USER_LOCATION_KEY = "location";
@ -461,7 +460,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
}
LookupTrigger trigger = (LookupTrigger) reply.property(LOOKUP_TRIGGER_KEY).toInt();
LookupTrigger trigger = (LookupTrigger) reply->property(LOOKUP_TRIGGER_KEY).toInt();
// set our current root place id to the ID that came back
@ -495,7 +494,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
}
// check if we had a path to override the path returned
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
QString overridePath = reply->property(OVERRIDE_PATH_KEY).toString();
if (!overridePath.isEmpty() && overridePath != "/") {
// make sure we don't re-handle an overriden path if this was a refresh of info from API
@ -543,10 +542,10 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
}
}
void AddressManager::handleAPIError(QNetworkReply& errorReply) {
qCDebug(networking) << "AddressManager API error -" << errorReply.error() << "-" << errorReply.errorString();
void AddressManager::handleAPIError(QNetworkReply* errorReply) {
qCDebug(networking) << "AddressManager API error -" << errorReply->error() << "-" << errorReply->errorString();
if (errorReply.error() == QNetworkReply::ContentNotFoundError) {
if (errorReply->error() == QNetworkReply::ContentNotFoundError) {
// if this is a lookup that has no result, don't keep re-trying it
_previousLookup.clear();
@ -874,14 +873,14 @@ QString AddressManager::getDomainID() const {
return DependencyManager::get<NodeList>()->getDomainHandler().getUUID().toString();
}
void AddressManager::handleShareableNameAPIResponse(QNetworkReply& requestReply) {
void AddressManager::handleShareableNameAPIResponse(QNetworkReply* requestReply) {
// make sure that this response is for the domain we're currently connected to
auto domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
if (requestReply.url().toString().contains(uuidStringWithoutCurlyBraces(domainID))) {
if (requestReply->url().toString().contains(uuidStringWithoutCurlyBraces(domainID))) {
// check for a name or default name in the API response
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject responseObject = QJsonDocument::fromJson(requestReply->readAll()).object();
QJsonObject domainObject = responseObject["domain"].toObject();
const QString DOMAIN_NAME_KEY = "name";
@ -917,7 +916,7 @@ void AddressManager::lookupShareableNameForDomainID(const QUuid& domainID) {
// no error callback handling
// in the case of an error we simply assume there is no default place name
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "handleShareableNameAPIResponse";
DependencyManager::get<AccountManager>()->sendRequest(GET_DOMAIN_ID.arg(uuidStringWithoutCurlyBraces(domainID)),

View file

@ -417,13 +417,13 @@ signals:
void goForwardPossible(bool isPossible);
private slots:
void handleAPIResponse(QNetworkReply& requestReply);
void handleAPIError(QNetworkReply& errorReply);
void handleAPIResponse(QNetworkReply* requestReply);
void handleAPIError(QNetworkReply* errorReply);
void handleShareableNameAPIResponse(QNetworkReply& requestReply);
void handleShareableNameAPIResponse(QNetworkReply* requestReply);
private:
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply* reply);
// Set host and port, and return `true` if it was changed.
bool setHost(const QString& host, LookupTrigger trigger, quint16 port = 0);

View file

@ -549,7 +549,7 @@ void NodeList::handleICEConnectionToDomainServer() {
_domainHandler.getICEClientID(),
_domainHandler.getPendingDomainID());
}
}
}
void NodeList::pingPunchForDomainServer() {
// make sure if we're here that we actually still need to ping the domain-server

View file

@ -65,7 +65,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
// if no callbacks specified, call our owns
if (params.isEmpty()) {
params.errorCallbackReceiver = this;
params.callbackReceiver = this;
params.errorCallbackMethod = "requestError";
}
@ -75,8 +75,8 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
params, NULL, multipart);
}
void UserActivityLogger::requestError(QNetworkReply& errorReply) {
qCDebug(networking) << errorReply.error() << "-" << errorReply.errorString();
void UserActivityLogger::requestError(QNetworkReply* errorReply) {
qCDebug(networking) << errorReply->error() << "-" << errorReply->errorString();
}
void UserActivityLogger::launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime) {

View file

@ -50,7 +50,7 @@ public slots:
void wentTo(AddressManager::LookupTrigger trigger, QString destinationType, QString destinationName);
private slots:
void requestError(QNetworkReply& errorReply);
void requestError(QNetworkReply* errorReply);
private:
UserActivityLogger();

View file

@ -312,11 +312,26 @@ void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotio
assert(state);
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
if (!serverlessMode) {
entityState->handleDeactivation();
}
EntityItemPointer entity = entityState->getEntity();
_entitiesToSort.insert(entity);
if (!serverlessMode) {
if (entity->getClientOnly()) {
switch (entityState->getOwnershipState()) {
case EntityMotionState::OwnershipState::PendingBid:
_bids.removeFirst(entityState);
entityState->clearOwnershipState();
break;
case EntityMotionState::OwnershipState::LocallyOwned:
_owned.removeFirst(entityState);
entityState->clearOwnershipState();
break;
default:
break;
}
} else {
entityState->handleDeactivation();
}
}
}
}
}

View file

@ -37,6 +37,14 @@ public:
}
pop_back();
}
void removeFirst(EntityMotionState* state) {
for (uint32_t i = 0; i < size(); ++i) {
if ((*this)[i] == state) {
remove(i);
break;
}
}
}
};
class PhysicalEntitySimulation : public EntitySimulation {

View file

@ -286,6 +286,13 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource,
if (QThread::currentThread() != thread()) {
qFatal("Called load on a non-surface thread");
}
// For desktop toolbar mode window: stop script when window is closed.
if (qmlSource.isEmpty()) {
getSurfaceContext()->engine()->quit();
return;
}
// Synchronous loading may take a while; restart the deadlock timer
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);

View file

@ -472,7 +472,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
const graphics::HazePointer& haze,
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer,
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource) {
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource,
bool renderShadows) {
auto args = renderContext->args;
auto& batch = (*args->_batch);
@ -554,7 +555,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Check if keylight casts shadows
bool keyLightCastShadows { false };
if (lightStage && lightStage->_currentFrame._sunLights.size()) {
if (renderShadows && lightStage && lightStage->_currentFrame._sunLights.size()) {
graphics::LightPointer keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
if (keyLight) {
keyLightCastShadows = keyLight->getCastShadows();
@ -711,11 +712,6 @@ void RenderDeferredCleanup::run(const render::RenderContextPointer& renderContex
}
}
RenderDeferred::RenderDeferred() {
}
void RenderDeferred::configure(const Config& config) {
}
@ -742,7 +738,7 @@ void RenderDeferred::run(const RenderContextPointer& renderContext, const Inputs
args->_batch = &batch;
_gpuTimer->begin(batch);
setupJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, haze, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource);
setupJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, haze, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource, _renderShadows);
lightsJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, lightClusters);

View file

@ -127,7 +127,8 @@ public:
const graphics::HazePointer& haze,
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer,
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource);
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource,
bool renderShadows);
};
class RenderDeferredLocals {
@ -166,7 +167,8 @@ public:
using Config = RenderDeferredConfig;
using JobModel = render::Job::ModelI<RenderDeferred, Inputs, Config>;
RenderDeferred();
RenderDeferred() {}
RenderDeferred(bool renderShadows) : _renderShadows(renderShadows) {}
void configure(const Config& config);
@ -178,6 +180,9 @@ public:
protected:
gpu::RangeTimerPointer _gpuTimer;
private:
bool _renderShadows { false };
};
class DefaultLightingSetup {

View file

@ -401,26 +401,34 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
for (auto& triangleSet : _modelSpaceMeshTriangleSets) {
float triangleSetDistance = 0.0f;
BoxFace triangleSetFace;
Triangle triangleSetTriangle;
if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) {
int shapeID = 0;
for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) {
int partIndex = 0;
for (auto &partTriangleSet : meshTriangleSets) {
float triangleSetDistance = 0.0f;
BoxFace triangleSetFace;
Triangle triangleSetTriangle;
if (partTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) {
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
float worldDistance = glm::distance(origin, worldIntersectionPoint);
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
float worldDistance = glm::distance(origin, worldIntersectionPoint);
if (worldDistance < bestDistance) {
bestDistance = worldDistance;
intersectedSomething = true;
face = triangleSetFace;
bestModelTriangle = triangleSetTriangle;
bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix;
extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint);
extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint);
bestSubMeshIndex = subMeshIndex;
if (worldDistance < bestDistance) {
bestDistance = worldDistance;
intersectedSomething = true;
face = triangleSetFace;
bestModelTriangle = triangleSetTriangle;
bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix;
extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint);
extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint);
extraInfo["partIndex"] = partIndex;
extraInfo["shapeID"] = shapeID;
bestSubMeshIndex = subMeshIndex;
}
}
partIndex++;
shapeID++;
}
subMeshIndex++;
}
@ -485,12 +493,14 @@ bool Model::convexHullContains(glm::vec3 point) {
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
const AABox& box = triangleSet.getBounds();
if (box.contains(meshFramePoint)) {
if (triangleSet.convexHullContains(meshFramePoint)) {
// It's inside this mesh, return true.
return true;
for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) {
for (auto &partTriangleSet : meshTriangleSets) {
const AABox& box = partTriangleSet.getBounds();
if (box.contains(meshFramePoint)) {
if (partTriangleSet.convexHullContains(meshFramePoint)) {
// It's inside this mesh, return true.
return true;
}
}
}
}
@ -653,9 +663,15 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
for (int i = 0; i < numberOfMeshes; i++) {
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.parts.size(); j++) {
const int numberOfParts = mesh.parts.size();
auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i];
meshTriangleSets.resize(numberOfParts);
for (int j = 0; j < numberOfParts; j++) {
const FBXMeshPart& part = mesh.parts.at(j);
auto& partTriangleSet = meshTriangleSets[j];
const int INDICES_PER_TRIANGLE = 3;
const int INDICES_PER_QUAD = 4;
const int TRIANGLES_PER_QUAD = 2;
@ -664,7 +680,7 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris;
_modelSpaceMeshTriangleSets[i].reserve(totalTriangles);
partTriangleSet.reserve(totalTriangles);
auto meshTransform = geometry.offset * mesh.modelTransform;
@ -686,8 +702,8 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
Triangle tri1 = { v0, v1, v3 };
Triangle tri2 = { v1, v2, v3 };
_modelSpaceMeshTriangleSets[i].insert(tri1);
_modelSpaceMeshTriangleSets[i].insert(tri2);
partTriangleSet.insert(tri1);
partTriangleSet.insert(tri2);
}
}
@ -706,7 +722,7 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f));
Triangle tri = { v0, v1, v2 };
_modelSpaceMeshTriangleSets[i].insert(tri);
partTriangleSet.insert(tri);
}
}
}
@ -876,56 +892,58 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, true, true);
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
auto box = triangleSet.getBounds();
for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) {
for (auto &partTriangleSet : meshTriangleSets) {
auto box = partTriangleSet.getBounds();
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
}
QVector<glm::vec3> points;
glm::vec3 brn = box.getCorner();
glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0);
glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z);
glm::vec3 blf = brn + glm::vec3(box.getDimensions().x, 0, box.getDimensions().z);
glm::vec3 trn = brn + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tln = bln + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 trf = brf + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tlf = blf + glm::vec3(0, box.getDimensions().y, 0);
points << brn << bln;
points << brf << blf;
points << brn << brf;
points << bln << blf;
points << trn << tln;
points << trf << tlf;
points << trn << trf;
points << tln << tlf;
points << brn << trn;
points << brf << trf;
points << bln << tln;
points << blf << tlf;
glm::vec4 color[] = {
{ 0.0f, 1.0f, 0.0f, 1.0f }, // green
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 0.0f, 1.0f, 1.0f }, // blue
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
{ 0.0f, 1.0f, 1.0f, 1.0f }, // cyan
{ 1.0f, 1.0f, 1.0f, 1.0f }, // white
{ 0.0f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.5f, 0.5f, 1.0f } };
DependencyManager::get<GeometryCache>()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID);
colorNdx++;
}
QVector<glm::vec3> points;
glm::vec3 brn = box.getCorner();
glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0);
glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z);
glm::vec3 blf = brn + glm::vec3(box.getDimensions().x, 0, box.getDimensions().z);
glm::vec3 trn = brn + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tln = bln + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 trf = brf + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tlf = blf + glm::vec3(0, box.getDimensions().y, 0);
points << brn << bln;
points << brf << blf;
points << brn << brf;
points << bln << blf;
points << trn << tln;
points << trf << tlf;
points << trn << trf;
points << tln << tlf;
points << brn << trn;
points << brf << trf;
points << bln << tln;
points << blf << tlf;
glm::vec4 color[] = {
{ 0.0f, 1.0f, 0.0f, 1.0f }, // green
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 0.0f, 1.0f, 1.0f }, // blue
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
{ 0.0f, 1.0f, 1.0f, 1.0f }, // cyan
{ 1.0f, 1.0f, 1.0f, 1.0f }, // white
{ 0.0f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.5f, 0.5f, 1.0f } };
DependencyManager::get<GeometryCache>()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID);
colorNdx++;
}
_mutex.unlock();
}

View file

@ -423,8 +423,7 @@ protected:
bool _overrideModelTransform { false };
bool _triangleSetsValid { false };
void calculateTriangleSets(const FBXGeometry& geometry);
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
std::vector<std::vector<TriangleSet>> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
virtual void createRenderItemSet();

View file

@ -61,7 +61,6 @@ protected:
class DrawOverlay3D {
public:
using Inputs = render::VaryingSet3<render::ItemBounds, LightingModelPointer, glm::vec2>;
using Config = DrawOverlay3DConfig;
using JobModel = render::Job::ModelI<DrawOverlay3D, Inputs, Config>;
@ -73,7 +72,7 @@ public:
protected:
render::ShapePlumberPointer _shapePlumber;
int _maxDrawn; // initialized by Config
bool _opaquePass{ true };
bool _opaquePass { true };
};
class CompositeHUD {

View file

@ -64,8 +64,8 @@ void RenderDeferredTask::configure(const Config& config)
}
const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, const char* selectionName,
const render::Varying& metas,
const render::Varying& opaques,
const render::Varying& metas,
const render::Varying& opaques,
const render::Varying& transparents) {
const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), std::string()).asVarying();
const auto selectedMetas = task.addJob<SelectItems>("MetaSelection", selectMetaInput, selectionName);
@ -75,7 +75,7 @@ const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, cons
return task.addJob<SelectItems>("TransparentSelection", selectItemInput, selectionName);
}
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, bool renderShadows) {
const auto& items = input.get<Input>();
auto fadeEffect = DependencyManager::get<FadeEffect>();
@ -168,7 +168,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource, lightClusters, hazeModel).asVarying();
task.addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs);
task.addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs, renderShadows);
// Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job

View file

@ -126,7 +126,7 @@ public:
RenderDeferredTask();
void configure(const Config& config);
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, bool renderShadows);
private:
static const render::Varying addSelectItemJobs(JobModel& task,

View file

@ -27,7 +27,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render:
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (isDeferred) {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
task.addJob<RenderDeferredTask>("RenderDeferredTask", items, true);
} else {
task.addJob<RenderForwardTask>("Forward", items);
}

View file

@ -23,6 +23,21 @@ void registerAudioMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue);
}
void AudioScriptingInterface::setLocalAudioInterface(AbstractAudioInterface* audioInterface) {
if (_localAudioInterface) {
disconnect(_localAudioInterface, &AbstractAudioInterface::isStereoInputChanged,
this, &AudioScriptingInterface::isStereoInputChanged);
}
_localAudioInterface = audioInterface;
if (_localAudioInterface) {
connect(_localAudioInterface, &AbstractAudioInterface::isStereoInputChanged,
this, &AudioScriptingInterface::isStereoInputChanged);
}
}
ScriptAudioInjector* AudioScriptingInterface::playSystemSound(SharedSoundPointer sound, const QVector3D& position) {
AudioInjectorOptions options;
options.position = glm::vec3(position.x(), position.y(), position.z());
@ -61,11 +76,10 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound
}
bool AudioScriptingInterface::setStereoInput(bool stereo) {
bool stereoInputChanged = false;
if (_localAudioInterface) {
stereoInputChanged = _localAudioInterface->setIsStereoInput(stereo);
QMetaObject::invokeMethod(_localAudioInterface, "setIsStereoInput", Q_ARG(bool, stereo));
}
return stereoInputChanged;
return true;
}
bool AudioScriptingInterface::isStereoInput() {

View file

@ -23,9 +23,11 @@ class AudioScriptingInterface : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(bool isStereoInput READ isStereoInput WRITE setStereoInput NOTIFY isStereoInputChanged)
public:
virtual ~AudioScriptingInterface() {}
void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; }
void setLocalAudioInterface(AbstractAudioInterface* audioInterface);
protected:
AudioScriptingInterface() {}
@ -52,7 +54,7 @@ protected:
/**jsdoc
* @function Audio.setStereoInput
* @param {boolean} stereo
* @returns {boolean}
* @returns {boolean}
*/
Q_INVOKABLE bool setStereoInput(bool stereo);
@ -114,6 +116,13 @@ signals:
*/
void inputReceived(const QByteArray& inputSamples);
/**jsdoc
* @function Audio.isStereoInputChanged
* @param {boolean} isStereo
* @returns {Signal}
*/
void isStereoInputChanged(bool isStereo);
private:
AbstractAudioInterface* _localAudioInterface { nullptr };
};

View file

@ -1639,22 +1639,24 @@ QVariantMap ScriptEngine::fetchModuleSource(const QString& modulePath, const boo
loader->start(MAX_RETRIES);
if (!loader->isFinished()) {
QTimer monitor;
QEventLoop loop;
QObject::connect(loader, &BatchLoader::finished, this, [&monitor, &loop]{
monitor.stop();
loop.quit();
// This lambda can get called AFTER this local scope has completed.
// This is why we pass smart ptrs to the lambda instead of references to local variables.
auto monitor = std::make_shared<QTimer>();
auto loop = std::make_shared<QEventLoop>();
QObject::connect(loader, &BatchLoader::finished, this, [monitor, loop] {
monitor->stop();
loop->quit();
});
// this helps detect the case where stop() is invoked during the download
// but not seen in time to abort processing in onload()...
connect(&monitor, &QTimer::timeout, this, [this, &loop]{
connect(monitor.get(), &QTimer::timeout, this, [this, loop] {
if (isStopping()) {
loop.exit(-1);
loop->exit(-1);
}
});
monitor.start(500);
loop.exec();
monitor->start(500);
loop->exec();
}
loader->deleteLater();
return req;

View file

@ -191,6 +191,7 @@ void ScriptEngines::shutdownScripting() {
// Gracefully stop the engine's scripting thread
scriptEngine->stop();
removeScriptEngine(scriptEngine);
// We need to wait for the engine to be done running before we proceed, because we don't
// want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing
@ -394,6 +395,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
// stop all scripts
qCDebug(scriptengine) << "stopping script..." << it.key();
scriptEngine->stop();
removeScriptEngine(scriptEngine);
}
// wait for engines to stop (ie: providing time for .scriptEnding cleanup handlers to run) before
// triggering reload of any Client scripts / Entity scripts
@ -441,6 +443,7 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) {
}
}
scriptEngine->stop();
removeScriptEngine(scriptEngine);
stoppedScript = true;
qCDebug(scriptengine) << "stopping script..." << scriptURL;
}

View file

@ -165,6 +165,10 @@ void SpatiallyNestable::forgetChild(SpatiallyNestablePointer newChild) const {
void SpatiallyNestable::setParentJointIndex(quint16 parentJointIndex) {
_parentJointIndex = parentJointIndex;
auto parent = _parent.lock();
if (parent) {
parent->recalculateChildCauterization();
}
}
glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position,

View file

@ -218,6 +218,7 @@ protected:
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const;
virtual void forgetChild(SpatiallyNestablePointer newChild) const;
virtual void recalculateChildCauterization() const { }
mutable ReadWriteLockable _childrenLock;
mutable QHash<QUuid, SpatiallyNestableWeakPointer> _children;

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <vector>
#include "AABox.h"

View file

@ -30,7 +30,7 @@ signals:
void rollingLogFile(QString newFilename);
protected:
void rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled = true);
void rollFileIfNecessary(QFile& file, bool force = false, bool notifyListenersIfRolled = true);
virtual bool processQueueItems(const Queue& messages) override;
private:
@ -79,12 +79,12 @@ FilePersistThread::FilePersistThread(const FileLogger& logger) : _logger(logger)
// A file may exist from a previous run - if it does, roll the file and suppress notifying listeners.
QFile file(_logger._fileName);
if (file.exists()) {
rollFileIfNecessary(file, false);
rollFileIfNecessary(file, true, false);
}
}
void FilePersistThread::rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled) {
if (file.size() > MAX_LOG_SIZE) {
void FilePersistThread::rollFileIfNecessary(QFile& file, bool force, bool notifyListenersIfRolled) {
if (force || (file.size() > MAX_LOG_SIZE)) {
QString newFileName = getLogRollerFilename();
if (file.copy(newFileName)) {
file.open(QIODevice::WriteOnly | QIODevice::Truncate);

View file

@ -83,7 +83,7 @@ void Tooltip::requestHyperlinkImage() {
auto accountManager = DependencyManager::get<AccountManager>();
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "handleAPIResponse";
accountManager->sendRequest(GET_PLACE.arg(_title),
@ -94,9 +94,9 @@ void Tooltip::requestHyperlinkImage() {
}
}
void Tooltip::handleAPIResponse(QNetworkReply& requestReply) {
void Tooltip::handleAPIResponse(QNetworkReply* requestReply) {
// did a preview image come back?
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject responseObject = QJsonDocument::fromJson(requestReply->readAll()).object();
QJsonObject dataObject = responseObject["data"].toObject();
const QString PLACE_KEY = "place";

View file

@ -49,7 +49,7 @@ signals:
void imageURLChanged();
private slots:
void handleAPIResponse(QNetworkReply& requestReply);
void handleAPIResponse(QNetworkReply* requestReply);
private:
void requestHyperlinkImage();

View file

@ -648,6 +648,26 @@ void TabletProxy::loadQMLSource(const QVariant& path, bool resizable) {
}
}
void TabletProxy::stopQMLSource() {
// For desktop toolbar mode dialogs.
if (!_toolbarMode || !_desktopWindow) {
qCDebug(uiLogging) << "tablet cannot clear QML because not desktop toolbar mode";
return;
}
auto root = _desktopWindow->asQuickItem();
if (root) {
QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, ""));
if (!_currentPathLoaded.toString().isEmpty()) {
emit screenChanged(QVariant("QML"), "");
}
_currentPathLoaded = "";
_state = State::Home;
} else {
qCDebug(uiLogging) << "tablet cannot clear QML because _desktopWindow is null";
}
}
bool TabletProxy::pushOntoStack(const QVariant& path) {
if (QThread::currentThread() != thread()) {
bool result = false;
@ -719,6 +739,7 @@ void TabletProxy::loadHomeScreen(bool forceOntoHomeScreen) {
// close desktop window
if (_desktopWindow->asQuickItem()) {
QMetaObject::invokeMethod(_desktopWindow->asQuickItem(), "setShown", Q_ARG(const QVariant&, QVariant(false)));
stopQMLSource(); // Stop the currently loaded QML running.
}
}
_state = State::Home;

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