Merge branch 'master' into address_dialog_focus_fix

This commit is contained in:
vladest 2018-06-29 19:41:26 +02:00
commit 0f1312ab9b
275 changed files with 6922 additions and 3047 deletions

View file

@ -27,7 +27,8 @@ android {
'-DRELEASE_TYPE=' + RELEASE_TYPE,
'-DSTABLE_BUILD=' + STABLE_BUILD,
'-DDISABLE_QML=OFF',
'-DDISABLE_KTX_CACHE=OFF'
'-DDISABLE_KTX_CACHE=OFF',
'-DUSE_BREAKPAD=' + (System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_TOKEN") ? 'ON' : 'OFF');
}
}
signingConfigs {
@ -46,6 +47,10 @@ android {
}
buildTypes {
debug {
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
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
@ -53,6 +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", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
}
}
@ -67,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

@ -67,6 +67,12 @@
android:name=".SplashActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Translucent.NoActionBar" />
<service
android:name=".BreakpadUploaderService"
android:enabled="true"
android:exported="false"
android:process=":breakpad_uploader"/>
</application>
<uses-feature android:name="android.software.vr.mode" android:required="true"/>

View file

@ -228,7 +228,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);
@ -270,18 +270,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

@ -0,0 +1,173 @@
package io.highfidelity.hifiinterface;
import android.app.Service;
import android.content.Intent;
import android.os.FileObserver;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.HttpsURLConnection;
public class BreakpadUploaderService extends Service {
private static final String ANNOTATIONS_JSON = "annotations.json";
private static final String TAG = "Interface";
public static final String EXT_DMP = "dmp";
private static final long DUMP_DELAY = 5000;
private FileObserver fileObserver;
public BreakpadUploaderService() {
super();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
URL baseUrl;
baseUrl = getUrl();
if (baseUrl == null) {
stopSelf();
return START_NOT_STICKY;
}
new Thread(() -> {
File[] matchingFiles = getFilesByExtension(getObbDir(), EXT_DMP);
for (File file : matchingFiles) {
uploadDumpAndDelete(file, baseUrl);
}
}).start();
fileObserver = new FileObserver(getObbDir().getPath()) {
@Override
public void onEvent(int event, String path) {
if (path == null) {
return;
}
if (FileObserver.CREATE == event && EXT_DMP.equals(getExtension(path))) {
URL baseUrl = getUrl();
if (baseUrl != null) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
uploadDumpAndDelete(new File(getObbDir(), path), baseUrl);
}
}, DUMP_DELAY);
}
}
}
};
fileObserver.startWatching();
return START_STICKY;
}
private URL getUrl() {
String parameters = getAnnotationsAsUrlEncodedParameters();
try {
return new URL(BuildConfig.BACKTRACE_URL+ "/post?format=minidump&token=" + BuildConfig.BACKTRACE_TOKEN + (parameters.isEmpty() ? "" : ("&" + parameters)));
} catch (MalformedURLException e) {
Log.e(TAG, "Could not initialize Breakpad URL", e);
}
return null;
}
private void uploadDumpAndDelete(File file, URL url) {
int size = (int) file.length();
byte[] bytes = new byte[size];
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
buf.read(bytes, 0, bytes.length);
buf.close();
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setChunkedStreamingMode(0);
OutputStream ostream = urlConnection.getOutputStream();
OutputStream out = new BufferedOutputStream(ostream);
out.write(bytes, 0, size);
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
in.read();
if (urlConnection.getResponseCode() == 200) {
file.delete();
}
urlConnection.disconnect();
} catch (IOException e) {
Log.e(TAG, "Error uploading file " + file.getAbsolutePath(), e);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private File[] getFilesByExtension(File dir, final String extension) {
return dir.listFiles(pathName -> getExtension(pathName.getName()).equals(extension));
}
private String getExtension(String fileName) {
String extension = "";
int i = fileName.lastIndexOf('.');
int p = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
if (i > p) {
extension = fileName.substring(i+1);
}
return extension;
}
public String getAnnotationsAsUrlEncodedParameters() {
String parameters = "";
File annotationsFile = new File(getObbDir(), ANNOTATIONS_JSON);
if (annotationsFile.exists()) {
JsonParser parser = new JsonParser();
try {
JsonObject json = (JsonObject) parser.parse(new FileReader(annotationsFile));
for (String k: json.keySet()) {
if (!json.get(k).getAsString().isEmpty()) {
String key = k.contains("/") ? k.substring(k.indexOf("/") + 1) : k;
if (!parameters.isEmpty()) {
parameters += "&";
}
parameters += URLEncoder.encode(key, "UTF-8") + "=" + URLEncoder.encode(json.get(k).getAsString(), "UTF-8");
}
}
} catch (FileNotFoundException e) {
Log.e(TAG, "Error reading annotations file", e);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Error reading annotations file", e);
}
}
return parameters;
}
}

View file

@ -56,6 +56,8 @@ public class InterfaceActivity extends QtActivity {
private AssetManager assetManager;
private static boolean inVrMode;
private boolean nativeEnterBackgroundCallEnqueued = false;
// private GvrApi gvrApi;
// Opaque native pointer to the Application C++ object.
// This object is owned by the InterfaceActivity instance and passed to the native methods.
@ -121,13 +123,18 @@ public class InterfaceActivity extends QtActivity {
@Override
protected void onPause() {
super.onPause();
nativeEnterBackground();
if (super.isLoading) {
nativeEnterBackgroundCallEnqueued = true;
} else {
nativeEnterBackground();
}
//gvrApi.pauseTracking();
}
@Override
protected void onStart() {
super.onStart();
nativeEnterBackgroundCallEnqueued = false;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
@ -256,6 +263,9 @@ public class InterfaceActivity extends QtActivity {
public void onAppLoadedComplete() {
super.isLoading = false;
if (nativeEnterBackgroundCallEnqueued) {
nativeEnterBackground();
}
}
public void performHapticFeedback(int duration) {

View file

@ -1,13 +1,13 @@
package io.highfidelity.hifiinterface;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.DialogInterface;
import android.app.AlertDialog;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
@ -24,20 +24,31 @@ public class PermissionChecker extends Activity {
private static final int REQUEST_PERMISSIONS = 20;
private static final boolean CHOOSE_AVATAR_ON_STARTUP = false;
private static final String TAG = "Interface";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent myIntent = new Intent(this, BreakpadUploaderService.class);
startService(myIntent);
if (CHOOSE_AVATAR_ON_STARTUP) {
showMenu();
}
this.requestAppPermissions(new
String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA}
,2,REQUEST_PERMISSIONS);
File obbDir = getObbDir();
if (!obbDir.exists()) {
if (obbDir.mkdirs()) {
Log.d(TAG, "Obb dir created");
}
}
requestAppPermissions(new
String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA}
,2,REQUEST_PERMISSIONS);
}
@ -124,6 +135,4 @@ public class PermissionChecker extends Activity {
launchActivityWithPermissions();
}
}
}

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'
@ -149,6 +151,13 @@ def packages = [
file: 'etc2comp-patched-armv8-libcpp.tgz',
versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
checksum: '14b02795d774457a33bbc60e00a786bc'
],
breakpad: [
file: 'breakpad.tgz',
versionId: '8VrYXz7oyc.QBxNia0BVJOUBvrFO61jI',
checksum: 'ddcb23df336b08017042ba4786db1d9e',
sharedLibFolder: 'lib',
includeLibs: ['libbreakpad_client.a']
]
]
@ -367,6 +376,7 @@ task verifyPolyvox(type: Verify) { def p = packages['polyvox']; src new File(bas
task verifyTBB(type: Verify) { def p = packages['tbb']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyHifiAC(type: Verify) { def p = packages['hifiAC']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyEtc2Comp(type: Verify) { def p = packages['etc2comp']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyBreakpad(type: Verify) { def p = packages['breakpad']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyDependencyDownloads(dependsOn: downloadDependencies) { }
verifyDependencyDownloads.dependsOn verifyQt
@ -378,6 +388,7 @@ verifyDependencyDownloads.dependsOn verifyPolyvox
verifyDependencyDownloads.dependsOn verifyTBB
verifyDependencyDownloads.dependsOn verifyHifiAC
verifyDependencyDownloads.dependsOn verifyEtc2Comp
verifyDependencyDownloads.dependsOn verifyBreakpad
task extractDependencies(dependsOn: verifyDependencyDownloads) {
doLast {
@ -540,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
@ -615,4 +712,4 @@ task testElf (dependsOn: 'externalNativeBuildDebug') {
}
}
}
*/
*/

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

@ -33,23 +33,26 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp debug library")
if (WIN32 OR APPLE)
if (WIN32)
set(_LIB_FILE "EtcLib.lib")
else ()
set(_LIB_FILE "libEtcLib.a")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/${_LIB_FILE} CACHE FILEPATH "Path to Etc2Comp debug library")
# use generator expression to ensure the correct library is found when building different configurations in VS
set(_LIB_FOLDER "$<$<CONFIG:RelWithDebInfo>:build/EtcLib/RelWithDebInfo>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<CONFIG:MinSizeRel>:build/EtcLib/MinSizeRel>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<OR:$<CONFIG:Release>,$<CONFIG:Debug>>:build/EtcLib/Release>")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp release library")
elseif (APPLE)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/libEtcLib.a CACHE FILEPATH "Path to EtcLib debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/Release/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/${_LIB_FILE} CACHE FILEPATH "Path to Etc2Comp release library")
else ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to EtcLib debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library")
endif ()
set(ETC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/Etc CACHE FILEPATH "Path to Etc2Comp/Etc include directory")
set(ETCCODEC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/EtcCodec CACHE FILEPATH "Path to Etc2Comp/EtcCodec include directory")
# ETC2COMP_INCLUDE_DIRS will be set later by FindEtc2Comp
# ETC2COMP_INCLUDE_DIRS will be set later by FindEtc2Comp

View file

@ -0,0 +1,22 @@
#
# Copyright 2018 High Fidelity, Inc.
# Created by Gabriel Calero & Cristian Duarte on 2018/03/13
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_BREAKPAD)
if (ANDROID)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/breakpad)
set(BREAKPAD_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE TYPE INTERNAL)
set(LIB_DIR ${INSTALL_DIR}/lib)
list(APPEND BREAKPAD_LIBRARIES ${LIB_DIR}/libbreakpad_client.a)
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BREAKPAD_INCLUDE_DIRS})
if (USE_BREAKPAD)
add_definitions(-DHAS_BREAKPAD)
endif()
endif()
target_link_libraries(${TARGET_NAME} ${BREAKPAD_LIBRARIES})
endmacro()

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

@ -206,14 +206,13 @@ endif()
# link required hifi libraries
link_hifi_libraries(
shared task octree ktx gpu gl procedural graphics graphics-scripting render
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
pointers
recording fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
workload
${PLATFORM_GL_BACKEND}
)
@ -226,6 +225,7 @@ target_openssl()
target_bullet()
target_opengl()
add_crashpad()
target_breakpad()
# perform standard include and linking for found externals
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})

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

@ -62,8 +62,6 @@ Original.Button {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
@ -78,8 +76,6 @@ Original.Button {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}

View file

@ -11,7 +11,6 @@ Item {
height: parent !== null ? parent.height : undefined
property var parentStackItem: null
property int headerHeight: 70
property string url
property string scriptURL
property bool keyboardEnabled: false
property bool keyboardRaised: false
@ -23,6 +22,7 @@ Item {
property bool punctuationMode: false
property bool passwordField: false
property bool isDesktop: false
property alias url: web.url
property alias webView: web.webViewCore
property alias profile: web.webViewCoreProfile
property bool remove: false
@ -81,7 +81,7 @@ Item {
color: hifi.colors.baseGray
font.pixelSize: 12
verticalAlignment: Text.AlignLeft
text: root.url
text: web.url
anchors {
top: nav.bottom
horizontalCenter: parent.horizontalCenter;
@ -131,11 +131,11 @@ Item {
function loadUrl(url) {
web.webViewCore.url = url
root.url = web.webViewCore.url;
}
onUrlChanged: {
loadUrl(url);
Rectangle {
anchors.fill: web;
color: hifi.colors.white;
}
FlickableWebViewCore {

View file

@ -87,8 +87,8 @@ Column {
description: description,
online_users: data.details.connections || data.details.concurrency || 0,
// Server currently doesn't give isStacked (undefined). Could give bool.
drillDownToPlace: (data.isStacked === undefined) ? (data.action !== 'concurrency') : data.isStacked,
isStacked: !!data.isStacked
drillDownToPlace: data.is_stacked || (data.action === 'concurrency'),
isStacked: !!data.is_stacked
};
}

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

@ -76,7 +76,7 @@ Item {
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2;
width: parent.width/2 - anchors.leftMargin;
height: 80;
}
@ -252,60 +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're zero.
visible: transactionHistoryModel.count === 0 && transactionHistoryModel.currentPageToRetrieve < 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;
@ -407,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,31 +10,11 @@
//
#include "AndroidHelper.h"
#include <QDebug>
#include <AccountManager.h>
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) {

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);
void notifyLoadComplete();
void notifyEnterForeground();
@ -31,7 +28,6 @@ public:
void performHapticFeedback(int duration);
QSharedPointer<AccountManager> getAccountManager() { return _accountManager; }
AndroidHelper(AndroidHelper const&) = delete;
void operator=(AndroidHelper const&) = delete;
@ -49,8 +45,6 @@ signals:
private:
AndroidHelper();
~AndroidHelper();
QSharedPointer<AccountManager> _accountManager;
QThread workerThread;
};
#endif

View file

@ -150,8 +150,8 @@
#include "audio/AudioScope.h"
#include "avatar/AvatarManager.h"
#include "avatar/MyHead.h"
#include "CrashRecoveryHandler.h"
#include "CrashHandler.h"
#include "Crashpad.h"
#include "devices/DdeFaceTracker.h"
#include "DiscoverabilityManager.h"
#include "GLCanvas.h"
@ -282,7 +282,9 @@ public:
private:
void initialize() {
setObjectName("Render");
PROFILE_SET_THREAD_NAME("Render");
setCrashAnnotation("render_thread_id", std::to_string((size_t)QThread::currentThreadId()));
if (!_renderContext->makeCurrent()) {
qFatal("Unable to make rendering context current on render thread");
}
@ -791,7 +793,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
bool previousSessionCrashed { false };
if (!inTestMode) {
previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
previousSessionCrashed = CrashRecoveryHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
}
// get dir to use for cache
@ -1079,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;
@ -1096,6 +1103,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_logger->setSessionID(accountManager->getSessionID());
setCrashAnnotation("metaverse_session_id", accountManager->getSessionID().toString().toStdString());
setCrashAnnotation("main_thread_id", std::to_string((size_t)QThread::currentThreadId()));
if (steamClient) {
qCDebug(interfaceapp) << "[VERSION] SteamVR buildID:" << steamClient->getSteamVRBuildID();
@ -1160,24 +1168,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
audioIO->startThread();
auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface, scripting::Audio>();
connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket);
connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected);
connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) {
auto audioClient = DependencyManager::get<AudioClient>();
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
auto myAvatarPosition = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldPosition();
float distance = glm::distance(myAvatarPosition, position);
if (distance < radius) {
audioClient->setMuted(true);
audioScriptingInterface->environmentMuted();
}
});
connect(this, &Application::activeDisplayPluginChanged,
reinterpret_cast<scripting::Audio*>(audioScriptingInterface.data()), &scripting::Audio::onContextChanged);
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@ -1241,16 +1231,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);
#endif
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
connect(accountManager.data(), &AccountManager::usernameChanged, [](QString username){
setCrashAnnotation("username", username.toStdString());
});
// set the account manager's root URL and trigger a login request if we don't have the access token
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(); });
@ -1366,6 +1351,28 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
initializeDisplayPlugins();
qCDebug(interfaceapp, "Initialized Display");
// An audio device changed signal received before the display plugins are set up will cause a crash,
// so we defer the setup of the `scripting::Audio` class until this point
{
auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface, scripting::Audio>();
connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket);
connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected);
connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) {
auto audioClient = DependencyManager::get<AudioClient>();
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
auto myAvatarPosition = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldPosition();
float distance = glm::distance(myAvatarPosition, position);
if (distance < radius) {
audioClient->setMuted(true);
audioScriptingInterface->environmentMuted();
}
});
connect(this, &Application::activeDisplayPluginChanged,
reinterpret_cast<scripting::Audio*>(audioScriptingInterface.data()), &scripting::Audio::onContextChanged);
}
// Create the rendering engine. This can be slow on some machines due to lots of
// GPU pipeline creation.
initializeRenderEngine();
@ -1751,6 +1758,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// set the local loopback interface for local sounds
AudioInjector::setLocalAudioInterface(audioIO.data());
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
audioScriptingInterface->setLocalAudioInterface(audioIO.data());
connect(audioIO.data(), &AudioClient::noiseGateOpened, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateOpened);
connect(audioIO.data(), &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed);
@ -2251,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();
@ -2289,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:
@ -2534,6 +2542,8 @@ Application::~Application() {
_main3DScene = nullptr;
_renderEngine = nullptr;
_gameWorkload.shutdown();
DependencyManager::destroy<Preferences>();
_entityClipboard->eraseAllOctreeElements();
@ -2990,6 +3000,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
surfaceContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
surfaceContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
surfaceContext->setContextProperty("Workload", _gameWorkload._engine->getConfiguration().get());
surfaceContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
@ -4529,6 +4540,10 @@ void Application::idle() {
qFatal("Unable to make main thread context current");
}
{
_gameWorkload.updateViews(_viewFrustum, getMyAvatar()->getHeadPosition());
_gameWorkload._engine->run();
}
{
PerformanceTimer perfTimer("update");
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
@ -4924,6 +4939,9 @@ void Application::init() {
avatar->setCollisionSound(sound);
}
}, Qt::QueuedConnection);
_gameWorkload.startup(getEntities()->getWorkloadSpace(), _main3DScene, _entitySimulation);
_entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace());
}
void Application::loadAvatarScripts(const QVector<QString>& urls) {
@ -5275,14 +5293,16 @@ void Application::setKeyboardFocusEntity(const EntityItemID& entityItemID) {
auto entityId = _keyboardFocusedEntity.get();
if (entities->wantsKeyboardFocus(entityId)) {
entities->setProxyWindow(entityId, _window->windowHandle());
auto entity = getEntities()->getEntity(entityId);
if (_keyboardMouseDevice->isActive()) {
_keyboardMouseDevice->pluginFocusOutEvent();
}
_lastAcceptedKeyPress = usecTimestampNow();
setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
auto entity = getEntities()->getEntity(entityId);
if (entity) {
setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
}
}
}
}
@ -5610,7 +5630,10 @@ void Application::update(float deltaTime) {
{
PROFILE_RANGE(simulation_physics, "Simulation");
PerformanceTimer perfTimer("simulation");
if (_physicsEnabled) {
auto t0 = std::chrono::high_resolution_clock::now();
auto t1 = t0;
{
PROFILE_RANGE(simulation_physics, "PrePhysics");
PerformanceTimer perfTimer("prePhysics)");
@ -5634,6 +5657,8 @@ void Application::update(float deltaTime) {
_entitySimulation->applyDynamicChanges();
t1 = std::chrono::high_resolution_clock::now();
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
avatarManager->getObjectsToAddToPhysics(motionStates);
@ -5646,6 +5671,7 @@ void Application::update(float deltaTime) {
dynamic->prepareForPhysicsSimulation();
});
}
auto t2 = std::chrono::high_resolution_clock::now();
{
PROFILE_RANGE(simulation_physics, "StepPhysics");
PerformanceTimer perfTimer("stepPhysics");
@ -5653,6 +5679,7 @@ void Application::update(float deltaTime) {
_physicsEngine->stepSimulation();
});
}
auto t3 = std::chrono::high_resolution_clock::now();
{
if (_physicsEngine->hasOutgoingChanges()) {
{
@ -5698,12 +5725,26 @@ void Application::update(float deltaTime) {
// NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework
_physicsEngine->dumpStatsIfNecessary();
}
auto t4 = std::chrono::high_resolution_clock::now();
if (!_aboutToQuit) {
// NOTE: the getEntities()->update() call below will wait for lock
// and will provide non-physical entity motion
getEntities()->update(true); // update the models...
}
auto t5 = std::chrono::high_resolution_clock::now();
workload::Timings timings(6);
timings[0] = (t4 - t0);
timings[1] = (t5 - t4);
timings[2] = (t4 - t3);
timings[3] = (t3 - t2);
timings[4] = (t2 - t1);
timings[5] = (t1 - t0);
_gameWorkload.updateSimulationTimings(timings);
}
}
} else {
@ -6165,6 +6206,8 @@ void Application::updateWindowTitle() const {
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
QString username = accountManager->getAccountInfo().getUsername();
setCrashAnnotation("username", username.toStdString());
QString currentPlaceName;
if (isServerlessMode()) {
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
@ -6555,6 +6598,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
scriptEngine->registerGlobalObject("Workload", _gameWorkload._engine->getConfiguration().get());
GraphicsScriptingInterface::registerMetaTypes(scriptEngine.data());
scriptEngine->registerGlobalObject("Graphics", DependencyManager::get<GraphicsScriptingInterface>().data());

View file

@ -72,6 +72,8 @@
#include "ui/overlays/Overlays.h"
#include "UndoStackScriptingInterface.h"
#include "workload/GameWorkload.h"
#include <procedural/ProceduralSkybox.h>
#include <graphics/Skybox.h>
#include <ModelScriptingInterface.h>
@ -274,6 +276,8 @@ public:
render::EnginePointer getRenderEngine() override { return _renderEngine; }
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
const GameWorkload& getGameWorkload() const { return _gameWorkload; }
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
void updateMyAvatarLookAtPosition();
@ -657,6 +661,8 @@ private:
render::EnginePointer _renderEngine{ new render::RenderEngine() };
gpu::ContextPointer _gpuContext; // initialized during window creation
GameWorkload _gameWorkload;
mutable QMutex _renderArgsMutex{ QMutex::Recursive };
struct AppRenderArgs {
render::Args _renderArgs;

View file

@ -77,7 +77,7 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
entity->setLastBroadcast(usecTimestampNow());
// since we're creating this object we will immediately volunteer to own its simulation
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);
entityProperties.setLastEdited(entity->getLastEdited());
} else {
qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree";

View file

@ -2,8 +2,8 @@
// CrashHandler.h
// interface/src
//
// Created by David Rowe on 24 Aug 2015.
// Copyright 2015 High Fidelity, Inc.
// Created by Clement Brisset on 01/19/18.
// Copyright 2018 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
@ -12,22 +12,10 @@
#ifndef hifi_CrashHandler_h
#define hifi_CrashHandler_h
#include <QString>
#include <string>
class CrashHandler {
bool startCrashHandler();
void setCrashAnnotation(std::string name, std::string value);
public:
static bool checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt = false);
private:
enum Action {
DELETE_INTERFACE_INI,
RETAIN_IMPORTANT_INFO,
DO_NOTHING
};
static Action promptUserForAction(bool showCrashMessage);
static void handleCrash(Action action);
};
#endif // hifi_CrashHandler_h
#endif

View file

@ -0,0 +1,79 @@
//
// CrashHandler_Breakpad.cpp
// interface/src
//
// Created by Clement Brisset on 01/19/18.
// Copyright 2018 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 "CrashHandler.h"
#if HAS_BREAKPAD
#include <mutex>
#include <client/linux/handler/exception_handler.h>
#include <client/linux/handler/minidump_descriptor.h>
#include <QDebug>
#include <QMap>
#include <QtCore/QFileInfo>
#include <QtAndroidExtras/QAndroidJniObject>
#include <SettingHelpers.h>
#include <BuildInfo.h>
google_breakpad::ExceptionHandler* gBreakpadHandler;
std::mutex annotationMutex;
QMap<QString, QString> annotations;
static bool breakpad_dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) {
return succeeded;
}
QString obbDir() {
QAndroidJniObject mediaDir = QAndroidJniObject::callStaticObjectMethod("android/os/Environment", "getExternalStorageDirectory", "()Ljava/io/File;");
QAndroidJniObject mediaPath = mediaDir.callObjectMethod( "getAbsolutePath", "()Ljava/lang/String;" );
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
QAndroidJniObject package = activity.callObjectMethod("getPackageName", "()Ljava/lang/String;");
QString dataAbsPath = mediaPath.toString()+"/Android/obb/" + package.toString();
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()),
nullptr, breakpad_dumpCallback, nullptr, true, -1);
return true;
}
void setCrashAnnotation(std::string name, std::string value) {
std::lock_guard<std::mutex> guard(annotationMutex);
QString qName = QString::fromStdString(name);
QString qValue = QString::fromStdString(value);
annotations[qName] = qValue;
flushAnnotations();
}
#endif

View file

@ -1,5 +1,5 @@
//
// Crashpad.cpp
// CrashHandler_Crashpad.cpp
// interface/src
//
// Created by Clement Brisset on 01/19/18.
@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Crashpad.h"
#include "CrashHandler.h"
#include <assert.h>
@ -117,14 +117,4 @@ void setCrashAnnotation(std::string name, std::string value) {
crashpadAnnotations->SetKeyValue(name, value);
}
#else
bool startCrashHandler() {
qDebug() << "No crash handler available.";
return false;
}
void setCrashAnnotation(std::string name, std::string value) {
}
#endif

View file

@ -0,0 +1,27 @@
//
// CrashHandler_None.cpp
// interface/src
//
// Created by Clement Brisset on 01/19/18.
// Copyright 2018 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 "CrashHandler.h"
#include <assert.h>
#include <QDebug>
#if !defined(HAS_CRASHPAD) && !defined(HAS_BREAKPAD)
bool startCrashHandler() {
qDebug() << "No crash handler available.";
return false;
}
void setCrashAnnotation(std::string name, std::string value) {
}
#endif

View file

@ -1,5 +1,5 @@
//
// CrashHandler.cpp
// CrashRecoveryHandler.cpp
// interface/src
//
// Created by David Rowe on 24 Aug 2015.
@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "CrashHandler.h"
#include "CrashRecoveryHandler.h"
#include <QCoreApplication>
#include <QDialog>
@ -30,7 +30,7 @@
#include <SettingHelpers.h>
bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt) {
bool CrashRecoveryHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt) {
QSettings::setDefaultFormat(JSON_FORMAT);
QSettings settings;
settings.beginGroup("Developer");
@ -59,7 +59,7 @@ bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPromp
return wasLikelyCrash;
}
CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
CrashRecoveryHandler::Action CrashRecoveryHandler::promptUserForAction(bool showCrashMessage) {
QDialog crashDialog;
QLabel* label;
if (showCrashMessage) {
@ -94,20 +94,20 @@ CrashHandler::Action CrashHandler::promptUserForAction(bool showCrashMessage) {
if (result == QDialog::Accepted) {
if (option1->isChecked()) {
return CrashHandler::DELETE_INTERFACE_INI;
return CrashRecoveryHandler::DELETE_INTERFACE_INI;
}
if (option2->isChecked()) {
return CrashHandler::RETAIN_IMPORTANT_INFO;
return CrashRecoveryHandler::RETAIN_IMPORTANT_INFO;
}
}
// Dialog cancelled or "do nothing" option chosen
return CrashHandler::DO_NOTHING;
return CrashRecoveryHandler::DO_NOTHING;
}
void CrashHandler::handleCrash(CrashHandler::Action action) {
if (action != CrashHandler::DELETE_INTERFACE_INI && action != CrashHandler::RETAIN_IMPORTANT_INFO) {
// CrashHandler::DO_NOTHING or unexpected value
void CrashRecoveryHandler::handleCrash(CrashRecoveryHandler::Action action) {
if (action != CrashRecoveryHandler::DELETE_INTERFACE_INI && action != CrashRecoveryHandler::RETAIN_IMPORTANT_INFO) {
// CrashRecoveryHandler::DO_NOTHING or unexpected value
return;
}
@ -126,7 +126,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
QUrl address;
bool tutorialComplete = false;
if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
if (action == CrashRecoveryHandler::RETAIN_IMPORTANT_INFO) {
// Read avatar info
// Location and orientation
@ -151,7 +151,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
settingsFile.remove();
}
if (action == CrashHandler::RETAIN_IMPORTANT_INFO) {
if (action == CrashRecoveryHandler::RETAIN_IMPORTANT_INFO) {
// Write avatar info
// Location and orientation

View file

@ -0,0 +1,33 @@
//
// CrashRecoveryHandler.h
// interface/src
//
// Created by David Rowe on 24 Aug 2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_CrashRecoveryHandler_h
#define hifi_CrashRecoveryHandler_h
#include <QString>
class CrashRecoveryHandler {
public:
static bool checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt = false);
private:
enum Action {
DELETE_INTERFACE_INI,
RETAIN_IMPORTANT_INFO,
DO_NOTHING
};
static Action promptUserForAction(bool showCrashMessage);
static void handleCrash(Action action);
};
#endif // hifi_CrashRecoveryHandler_h

View file

@ -1,20 +0,0 @@
//
// Crashpad.h
// interface/src
//
// Created by Clement Brisset on 01/19/18.
// Copyright 2018 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_Crashpad_h
#define hifi_Crashpad_h
#include <string>
bool startCrashHandler();
void setCrashAnnotation(std::string name, std::string value);
#endif // hifi_Crashpad_h

View file

@ -23,7 +23,7 @@
#include <UserActivityLogger.h>
#include <UUID.h>
#include "Crashpad.h"
#include "CrashHandler.h"
#include "Menu.h"
const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Connections;
@ -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");
@ -812,7 +812,7 @@ Menu::Menu() {
});
essLogAction->setEnabled(nodeList->getThisNodeCanRez());
action = addActionToQMenuAndActionHash(developerMenu, "Script Log (HMD friendly)...", Qt::NoButton,
addActionToQMenuAndActionHash(developerMenu, "Script Log (HMD friendly)...", Qt::NoButton,
qApp, SLOT(showScriptLogs()));
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VerboseLogging, 0, false,

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

@ -440,8 +440,8 @@ QVariantMap AvatarActionHold::getArguments() {
QVariantMap arguments = ObjectDynamic::getArguments();
withReadLock([&]{
arguments["holderID"] = _holderID;
arguments["relativePosition"] = glmToQMap(_relativePosition);
arguments["relativeRotation"] = glmToQMap(_relativeRotation);
arguments["relativePosition"] = vec3ToQMap(_relativePosition);
arguments["relativeRotation"] = quatToQMap(_relativeRotation);
arguments["timeScale"] = _linearTimeScale;
arguments["hand"] = _hand;
arguments["kinematic"] = _kinematic;

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

@ -21,6 +21,17 @@ AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisi
_type = MOTIONSTATE_TYPE_AVATAR;
}
void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
ObjectMotionState::handleEasyChanges(flags);
if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && !_body->isActive()) {
_body->activate();
}
}
bool AvatarMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
}
AvatarMotionState::~AvatarMotionState() {
assert(_avatar);
_avatar = nullptr;
@ -46,6 +57,9 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
const btCollisionShape* AvatarMotionState::computeNewShape() {
ShapeInfo shapeInfo;
std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
glm::vec3 halfExtents = shapeInfo.getHalfExtents();
halfExtents.y = 0.0f;
_diameter = 2.0f * glm::length(halfExtents);
return getShapeManager()->getShape(shapeInfo);
}
@ -60,25 +74,31 @@ void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
worldTrans.setRotation(glmToBullet(getObjectRotation()));
if (_body) {
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
_body->setAngularVelocity(glmToBullet(getObjectLinearVelocity()));
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
}
}
// virtual
void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
// HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform
// as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie
// the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later.
const float SPRING_TIMESCALE = 0.5f;
float tau = PHYSICS_ENGINE_FIXED_SUBSTEP / SPRING_TIMESCALE;
btVector3 currentPosition = worldTrans.getOrigin();
btVector3 targetPosition = glmToBullet(getObjectPosition());
btTransform newTransform;
newTransform.setOrigin((1.0f - tau) * currentPosition + tau * targetPosition);
newTransform.setRotation(glmToBullet(getObjectRotation()));
_body->setWorldTransform(newTransform);
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
_body->setAngularVelocity(glmToBullet(getObjectLinearVelocity()));
btVector3 offsetToTarget = glmToBullet(getObjectPosition()) - currentPosition;
float distance = offsetToTarget.length();
if ((1.0f - tau) * distance > _diameter) {
// the avatar body is far from its target --> slam position
btTransform newTransform;
newTransform.setOrigin(currentPosition + offsetToTarget);
newTransform.setRotation(glmToBullet(getObjectRotation()));
_body->setWorldTransform(newTransform);
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
} else {
// the avatar body is near its target --> slam velocity
btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget;
_body->setLinearVelocity(velocity);
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
}
}
// These pure virtual methods must be implemented for each MotionState type
@ -134,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();
@ -145,3 +169,8 @@ void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma
mask = Physics::getDefaultCollisionMask(group);
}
// virtual
float AvatarMotionState::getMass() const {
return std::static_pointer_cast<Avatar>(_avatar)->computeMass();
}

View file

@ -23,6 +23,9 @@ class AvatarMotionState : public ObjectMotionState {
public:
AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape);
virtual void handleEasyChanges(uint32_t& flags) override;
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
virtual uint32_t getIncomingDirtyFlags() override;
@ -56,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);
@ -64,6 +68,8 @@ public:
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
virtual float getMass() const override;
friend class AvatarManager;
friend class Avatar;
@ -76,6 +82,7 @@ protected:
virtual const btCollisionShape* computeNewShape() override;
AvatarSharedPointer _avatar;
float _diameter { 0.0f };
uint32_t _dirtyFlags;
};

View file

@ -121,6 +121,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_headData = new MyHead(this);
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
_skeletonModel->setLoadingPriority(MYAVATAR_LOADING_PRIORITY);
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) {
if (success) {
@ -531,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);
}
}
@ -544,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;
}
{
@ -684,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);
}
@ -1067,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();
}
@ -1218,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

@ -108,12 +108,15 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
btScalar lengthAxis = axis.length();
if (lengthAxis > FLT_EPSILON) {
// we're walking sideways
btScalar angle = acosf(lengthAxis / adjustedDirection.length());
if (rayDirection.dot(forward) < 0.0f) {
angle = PI - angle;
btScalar cosAngle = lengthAxis / adjustedDirection.length();
if (cosAngle < 1.0f) {
btScalar angle = acosf(cosAngle);
if (rayDirection.dot(forward) < 0.0f) {
angle = PI - angle;
}
axis /= lengthAxis;
rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation;
}
axis /= lengthAxis;
rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation;
} else if (rayDirection.dot(forward) < 0.0f) {
// we're walking backwards
rotation = btMatrix3x3(btQuaternion(_currentUp, PI)) * rotation;

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

@ -26,7 +26,7 @@
#include "AddressManager.h"
#include "Application.h"
#include "Crashpad.h"
#include "CrashHandler.h"
#include "InterfaceLogging.h"
#include "UserActivityLogger.h"
#include "MainWindow.h"

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

@ -319,6 +319,15 @@ public slots:
* {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted}
* are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings >
* General > Snapshots.
*
* If user has supplied a specific filename for the snapshot:
* If the user's requested filename has a suffix that's contained within SUPPORTED_IMAGE_FORMATS,
* DON'T append ".jpg" to the filename. QT will save the image in the format associated with the
* filename's suffix.
* If you want lossless Snapshots, supply a `.png` filename. Otherwise, use `.jpeg` or `.jpg`.
* Otherwise, ".jpg" is appended to the user's requested filename so that the image is saved in JPG format.
* If the user hasn't supplied a specific filename for the snapshot:
* Save the snapshot in JPG format according to FILENAME_PATH_FORMAT
* @function Window.takeSnapshot
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
* signal.

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

@ -50,6 +50,7 @@ const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";
const QString SNAPSHOTS_DIRECTORY = "Snapshots";
const QString URL = "highfidelity_url";
static const int SNAPSHOT_360_TIMER_INTERVAL = 350;
static const QList<QString> SUPPORTED_IMAGE_FORMATS = { "jpg", "jpeg", "png" };
Snapshot::Snapshot() {
_snapshotTimer.setSingleShot(false);
@ -67,7 +68,6 @@ Snapshot::Snapshot() {
}
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
if (!QFile(snapshotPath).exists()) {
return NULL;
}
@ -95,7 +95,6 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
}
QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) {
QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname);
if (snapshotFile) {
@ -122,11 +121,15 @@ static const glm::quat CAMERA_ORIENTATION_LEFT(glm::quat(glm::radians(glm::vec3(
static const glm::quat CAMERA_ORIENTATION_BACK(glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f))));
static const glm::quat CAMERA_ORIENTATION_RIGHT(glm::quat(glm::radians(glm::vec3(0.0f, 270.0f, 0.0f))));
static const glm::quat CAMERA_ORIENTATION_UP(glm::quat(glm::radians(glm::vec3(90.0f, 0.0f, 0.0f))));
void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {
void Snapshot::save360Snapshot(const glm::vec3& cameraPosition,
const bool& cubemapOutputFormat,
const bool& notify,
const QString& filename) {
_snapshotFilename = filename;
_notify360 = notify;
_cubemapOutputFormat = cubemapOutputFormat;
SecondaryCameraJobConfig* secondaryCameraRenderConfig = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
SecondaryCameraJobConfig* secondaryCameraRenderConfig =
static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
// Save initial values of secondary camera render config
_oldEnabled = secondaryCameraRenderConfig->isEnabled();
@ -141,9 +144,11 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cube
}
// Initialize some secondary camera render config options for 360 snapshot capture
static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(0);
static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))
->setCurve(0);
secondaryCameraRenderConfig->resetSizeSpectatorCamera(static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION), static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION));
secondaryCameraRenderConfig->resetSizeSpectatorCamera(static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION),
static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION));
secondaryCameraRenderConfig->setProperty("attachedEntityId", "");
secondaryCameraRenderConfig->setPosition(cameraPosition);
secondaryCameraRenderConfig->setProperty("vFoV", SNAPSHOT_360_FOV);
@ -159,7 +164,8 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cube
}
void Snapshot::takeNextSnapshot() {
SecondaryCameraJobConfig* config = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
SecondaryCameraJobConfig* config =
static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
// Order is:
// 0. Down
@ -191,7 +197,9 @@ void Snapshot::takeNextSnapshot() {
_snapshotTimer.stop();
// Reset secondary camera render config
static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(1);
static_cast<ToneMappingConfig*>(
qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))
->setCurve(1);
config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height());
config->setProperty("attachedEntityId", _oldAttachedEntityId);
config->setProperty("vFoV", _oldvFoV);
@ -338,8 +346,10 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true));
}
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, const QString& userSelectedPathname) {
QFile* Snapshot::savedFileForSnapshot(QImage& shot,
bool isTemporary,
const QString& userSelectedFilename,
const QString& userSelectedPathname) {
// adding URL to snapshot
QUrl currentURL = DependencyManager::get<AddressManager>()->currentPublicAddress();
shot.setText(URL, currentURL.toString());
@ -350,18 +360,35 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
QDateTime now = QDateTime::currentDateTime();
// If user has requested specific filename then use it, else create the filename
// 'jpg" is appended, as the image is saved in jpg format. This is the case for all snapshots
// (see definition of FILENAME_PATH_FORMAT)
// If user has supplied a specific filename for the snapshot:
// If the user's requested filename has a suffix that's contained within SUPPORTED_IMAGE_FORMATS,
// DON'T append ".jpg" to the filename. QT will save the image in the format associated with the
// filename's suffix.
// If you want lossless Snapshots, supply a `.png` filename. Otherwise, use `.jpeg` or `.jpg`.
// For PNGs, we use a "quality" of "50". The output image quality is the same as "100"
// is the same as "0" -- the difference lies in the amount of compression applied to the PNG,
// which slightly affects the time it takes to save the image.
// Otherwise, ".jpg" is appended to the user's requested filename so that the image is saved in JPG format.
// If the user hasn't supplied a specific filename for the snapshot:
// Save the snapshot in JPG format at "100" quality according to FILENAME_PATH_FORMAT
int imageQuality = 100;
QString filename;
if (!userSelectedFilename.isNull()) {
filename = userSelectedFilename + ".jpg";
QFileInfo snapshotFileInfo(userSelectedFilename);
QString userSelectedFilenameSuffix = snapshotFileInfo.suffix();
userSelectedFilenameSuffix = userSelectedFilenameSuffix.toLower();
if (SUPPORTED_IMAGE_FORMATS.contains(userSelectedFilenameSuffix)) {
filename = userSelectedFilename;
if (userSelectedFilenameSuffix == "png") {
imageQuality = 50;
}
} else {
filename = userSelectedFilename + ".jpg";
}
} else {
filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));
}
const int IMAGE_QUALITY = 100;
if (!isTemporary) {
// If user has requested specific path then use it, else use the application value
QString snapshotFullPath;
@ -372,11 +399,13 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
}
if (snapshotFullPath.isEmpty()) {
snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
snapshotFullPath =
OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory",
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
_snapshotsLocation.set(snapshotFullPath);
}
if (!snapshotFullPath.isEmpty()) { // not cancelled
if (!snapshotFullPath.isEmpty()) { // not cancelled
if (!snapshotFullPath.endsWith(QDir::separator())) {
snapshotFullPath.append(QDir::separator());
@ -393,7 +422,9 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
qApp->getApplicationCompositor().getReticleInterface()->setVisible(true);
qApp->getApplicationCompositor().getReticleInterface()->setAllowMouseCapture(true);
snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Write Error - Choose New Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
snapshotFullPath =
OffscreenUi::getExistingDirectory(nullptr, "Write Error - Choose New Snapshots Directory",
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
if (snapshotFullPath.isEmpty()) {
return NULL;
}
@ -407,12 +438,11 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
imageFile = new QFile(snapshotFullPath);
}
shot.save(imageFile, 0, IMAGE_QUALITY);
shot.save(imageFile, 0, imageQuality);
imageFile->close();
return imageFile;
}
}
// Either we were asked for a tempororary, or the user didn't set a directory.
QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename);
@ -423,18 +453,17 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
}
imageTempFile->setAutoRemove(isTemporary);
shot.save(imageTempFile, 0, IMAGE_QUALITY);
shot.save(imageTempFile, 0, imageQuality);
imageTempFile->close();
return imageTempFile;
}
void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) {
const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots";
QUrl url = href;
if (url.isEmpty()) {
SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(filename);
SnapshotMetaData* snapshotData = parseSnapshotData(filename);
if (snapshotData) {
url = snapshotData->getURL();
}
@ -444,7 +473,7 @@ void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) {
url = QUrl(DependencyManager::get<AddressManager>()->currentShareableAddress());
}
SnapshotUploader* uploader = new SnapshotUploader(url, filename);
QFile* file = new QFile(filename);
Q_ASSERT(file->exists());
file->open(QIODevice::ReadOnly);
@ -458,20 +487,16 @@ void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) {
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"image\"; filename=\"" + file->fileName() + "\""));
imagePart.setBodyDevice(file);
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(imagePart);
auto accountManager = DependencyManager::get<AccountManager>();
JSONCallbackParameters callbackParams(uploader, "uploadSuccess", uploader, "uploadFailure");
accountManager->sendRequest(SNAPSHOT_UPLOAD_URL,
AccountManagerAuth::Required,
QNetworkAccessManager::PostOperation,
callbackParams,
nullptr,
multiPart);
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(imagePart);
auto accountManager = DependencyManager::get<AccountManager>();
JSONCallbackParameters callbackParams(uploader, "uploadSuccess", "uploadFailure");
accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, AccountManagerAuth::Required, QNetworkAccessManager::PostOperation,
callbackParams, nullptr, multiPart);
}
QString Snapshot::getSnapshotsLocation() {

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

@ -490,9 +490,9 @@ void Stats::updateStats(bool force) {
};
for (int32_t j = 0; j < categories.size(); ++j) {
QString recordKey = "/idle/update/" + categories[j];
itr = allRecords.find(recordKey);
if (itr != allRecords.end()) {
float dt = (float)itr.value().getMovingAverage() / (float)USECS_PER_MSEC;
auto record = PerformanceTimer::getTimerRecord(recordKey);
if (record.getCount()) {
float dt = (float) record.getMovingAverage() / (float)USECS_PER_MSEC;
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);
idleUpdateStats.push(SortableStat(message, dt));
}

View file

@ -41,12 +41,16 @@ bool Billboardable::pointTransformAtCamera(Transform& transform, glm::quat offse
glm::vec3 cameraPos = qApp->getCamera().getPosition();
// use the referencial from the avatar, y isn't always up
glm::vec3 avatarUP = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation()*Vectors::UP;
glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP))));
transform.setRotation(rotation);
transform.postRotate(offsetRotation);
return true;
// check to see if glm::lookAt will work / using glm::lookAt variable name
glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP));
// make sure s is not NaN for any component
if(glm::length2(s) > 0.0f) {
glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP))));
transform.setRotation(rotation);
transform.postRotate(offsetRotation);
return true;
}
}
return false;
}

View file

@ -201,13 +201,12 @@ void Circle3DOverlay::render(RenderArgs* args) {
float tickMarkAngle = getMajorTickMarksAngle();
float angle = _startAt - fmodf(_startAt, tickMarkAngle) + tickMarkAngle;
float angleInRadians = glm::radians(angle);
float tickMarkLength = getMajorTickMarksLength();
float startRadius = (tickMarkLength > 0.0f) ? _innerRadius : _outerRadius;
float endRadius = startRadius + tickMarkLength;
while (angle <= _endAt) {
angleInRadians = glm::radians(angle);
float angleInRadians = glm::radians(angle);
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius);
@ -223,13 +222,12 @@ void Circle3DOverlay::render(RenderArgs* args) {
float tickMarkAngle = getMinorTickMarksAngle();
float angle = _startAt - fmodf(_startAt, tickMarkAngle) + tickMarkAngle;
float angleInRadians = glm::radians(angle);
float tickMarkLength = getMinorTickMarksLength();
float startRadius = (tickMarkLength > 0.0f) ? _innerRadius : _outerRadius;
float endRadius = startRadius + tickMarkLength;
while (angle <= _endAt) {
angleInRadians = glm::radians(angle);
float angleInRadians = glm::radians(angle);
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius);

View file

@ -41,8 +41,6 @@
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
Overlays::Overlays() {
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterPointerEvent);

View file

@ -254,6 +254,7 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
_webSurface->getSurfaceContext()->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);

View file

@ -0,0 +1,82 @@
//
// GameWorkload.cpp
//
// Created by Sam Gateau on 2/16/2018.
// Copyright 2018 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 "GameWorkload.h"
#include "GameWorkloadRenderer.h"
#include <ViewFrustum.h>
#include <workload/RegionTracker.h>
#include <workload/SpaceClassifier.h>
#include "PhysicsBoundary.h"
class WorkloadEngineBuilder {
public:
using Inputs = workload::VaryingSet2<workload::Views, workload::Timings>;
using Outputs = workload::RegionTracker::Outputs;
using JobModel = workload::Task::ModelIO<WorkloadEngineBuilder, Inputs, Outputs>;
void build(JobModel& model, const workload::Varying& in, workload::Varying& out) {
const auto& inViews = in.getN<Inputs>(0);
const auto& inTimings = in.getN<Inputs>(1);
const auto usedViews = model.addJob<workload::SetupViews>("setupViews", inViews);
const auto controlViewsIn = workload::ControlViews::Input(usedViews, inTimings).asVarying();
const auto fixedViews = model.addJob<workload::ControlViews>("controlViews", controlViewsIn);
const auto regionTrackerOut = model.addJob<workload::SpaceClassifierTask>("spaceClassifier", fixedViews);
model.addJob<PhysicsBoundary>("PhysicsBoundary", regionTrackerOut);
model.addJob<GameSpaceToRender>("SpaceToRender");
out = regionTrackerOut;
}
};
GameWorkloadContext::GameWorkloadContext(const workload::SpacePointer& space,
const render::ScenePointer& scene,
const PhysicalEntitySimulationPointer& simulation): WorkloadContext(space),
_scene(scene), _simulation(simulation)
{
}
GameWorkloadContext::~GameWorkloadContext() {
}
GameWorkload::GameWorkload() :
_engine(std::make_shared<workload::Engine>(WorkloadEngineBuilder::JobModel::create("Workload"), std::shared_ptr<GameWorkloadContext>()))
{
}
GameWorkload::~GameWorkload() {
shutdown();
}
void GameWorkload::startup(const workload::SpacePointer& space,
const render::ScenePointer& scene,
const PhysicalEntitySimulationPointer& simulation) {
_engine->reset(std::make_shared<GameWorkloadContext>(space, scene, simulation));
}
void GameWorkload::shutdown() {
_engine.reset();
}
void GameWorkload::updateViews(const ViewFrustum& frustum, const glm::vec3& headPosition) {
workload::Views views;
views.emplace_back(workload::View::evalFromFrustum(frustum, headPosition - frustum.getPosition()));
views.emplace_back(workload::View::evalFromFrustum(frustum));
_engine->feedInput<WorkloadEngineBuilder::Inputs>(0, views);
}
void GameWorkload::updateSimulationTimings(const workload::Timings& timings) {
_engine->feedInput<WorkloadEngineBuilder::Inputs>(1, timings);
}

View file

@ -0,0 +1,45 @@
//
// GameWorkload.h
//
// Created by Sam Gateau on 2/16/2018.
// Copyright 2018 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_GameWorkload_h
#define hifi_GameWorkload_h
#include <workload/Space.h>
#include <workload/Engine.h>
#include <render/Scene.h>
#include "PhysicalEntitySimulation.h"
class GameWorkloadContext : public workload::WorkloadContext {
public:
GameWorkloadContext(const workload::SpacePointer& space,
const render::ScenePointer& scene,
const PhysicalEntitySimulationPointer& simulation);
virtual ~GameWorkloadContext();
render::ScenePointer _scene;
PhysicalEntitySimulationPointer _simulation;
};
class GameWorkload {
public:
GameWorkload();
~GameWorkload();
void startup(const workload::SpacePointer& space,
const render::ScenePointer& scene,
const PhysicalEntitySimulationPointer& simulation);
void shutdown();
void updateViews(const ViewFrustum& frustum, const glm::vec3& headPosition);
void updateSimulationTimings(const workload::Timings& timings);
workload::EnginePointer _engine;
};
#endif // hifi_GameWorkload_h

View file

@ -0,0 +1,257 @@
//
// GameWorkloadRender.cpp
//
// Created by Sam Gateau on 2/20/2018.
// Copyright 2018 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 "GameWorkloadRenderer.h"
#include <cstring>
#include <gpu/Context.h>
#include <StencilMaskPass.h>
#include <GeometryCache.h>
#include "render-utils/drawWorkloadProxy_vert.h"
#include "render-utils/drawWorkloadView_vert.h"
#include "render-utils/drawWorkloadProxy_frag.h"
#include "render-utils/drawWorkloadView_frag.h"
void GameSpaceToRender::configure(const Config& config) {
_freezeViews = config.freezeViews;
_showAllProxies = config.showProxies;
_showAllViews = config.showViews;
}
void GameSpaceToRender::run(const workload::WorkloadContextPointer& runContext, Outputs& outputs) {
auto gameWorkloadContext = std::dynamic_pointer_cast<GameWorkloadContext>(runContext);
if (!gameWorkloadContext) {
return;
}
auto space = gameWorkloadContext->_space;
if (!space) {
return;
}
auto visible = _showAllProxies || _showAllViews;
auto showProxies = _showAllProxies;
auto showViews = _showAllViews;
auto freezeViews = _freezeViews;
render::Transaction transaction;
auto scene = gameWorkloadContext->_scene;
// Nothing really needed, early exit
if (!visible) {
if (render::Item::isValidID(_spaceRenderItemID)) {
transaction.updateItem<GameWorkloadRenderItem>(_spaceRenderItemID, [](GameWorkloadRenderItem& item) {
item.setVisible(false);
});
scene->enqueueTransaction(transaction);
}
return;
}
workload::Proxy::Vector proxies(space->getNumAllocatedProxies());
space->copyProxyValues(proxies.data(), (uint32_t)proxies.size());
workload::Views views(space->getNumViews());
space->copyViews(views);
// Valid space, let's display its content
if (!render::Item::isValidID(_spaceRenderItemID)) {
_spaceRenderItemID = scene->allocateID();
auto renderItem = std::make_shared<GameWorkloadRenderItem>();
renderItem->editBound().setBox(glm::vec3(-16000.0f), 32000.0f);
renderItem->setAllProxies(proxies);
transaction.resetItem(_spaceRenderItemID, std::make_shared<GameWorkloadRenderItem::Payload>(renderItem));
}
transaction.updateItem<GameWorkloadRenderItem>(_spaceRenderItemID, [visible, showProxies, proxies, freezeViews, showViews, views](GameWorkloadRenderItem& item) {
item.setVisible(visible);
item.showProxies(showProxies);
item.setAllProxies(proxies);
item.showViews(showViews);
item.setAllViews(views);
});
scene->enqueueTransaction(transaction);
}
namespace render {
template <> const ItemKey payloadGetKey(const GameWorkloadRenderItem::Pointer& payload) {
return payload->getKey();
}
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload) {
if (payload) {
return payload->getBound();
}
return Item::Bound();
}
template <> void payloadRender(const GameWorkloadRenderItem::Pointer& payload, RenderArgs* args) {
if (payload) {
payload->render(args);
}
}
template <> const ShapeKey shapeGetShapeKey(const GameWorkloadRenderItem::Pointer& payload) {
return ShapeKey::Builder::ownPipeline();
}
}
GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) {
}
render::ItemKey GameWorkloadRenderItem::getKey() const {
return _key;
}
void GameWorkloadRenderItem::setVisible(bool isVisible) {
if (isVisible) {
_key = render::ItemKey::Builder(_key).withVisible();
} else {
_key = render::ItemKey::Builder(_key).withInvisible();
}
}
void GameWorkloadRenderItem::showProxies(bool show) {
_showProxies = show;
}
void GameWorkloadRenderItem::showViews(bool show) {
_showViews = show;
}
void GameWorkloadRenderItem::setAllProxies(const workload::Proxy::Vector& proxies) {
_myOwnProxies = proxies;
static const uint32_t sizeOfProxy = sizeof(workload::Proxy);
if (!_allProxiesBuffer) {
_allProxiesBuffer = std::make_shared<gpu::Buffer>(sizeOfProxy);
}
_allProxiesBuffer->setData(proxies.size() * sizeOfProxy, (const gpu::Byte*) proxies.data());
_numAllProxies = (uint32_t) proxies.size();
}
void GameWorkloadRenderItem::setAllViews(const workload::Views& views) {
_myOwnViews = views;
static const uint32_t sizeOfView = sizeof(workload::View);
if (!_allViewsBuffer) {
_allViewsBuffer = std::make_shared<gpu::Buffer>(sizeOfView);
}
_allViewsBuffer->setData(views.size() * sizeOfView, (const gpu::Byte*) views.data());
_numAllViews = (uint32_t)views.size();
}
const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() {
if (!_drawAllProxiesPipeline) {
auto vs = drawWorkloadProxy_vert::getShader();
auto ps = drawWorkloadProxy_frag::getShader();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding("workloadProxiesBuffer", 0));
gpu::Shader::makeProgram(*program, slotBindings);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL);
/* state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);*/
PrepareStencil::testMaskDrawShape(*state);
state->setCullMode(gpu::State::CULL_NONE);
_drawAllProxiesPipeline = gpu::Pipeline::create(program, state);
}
return _drawAllProxiesPipeline;
}
const gpu::PipelinePointer GameWorkloadRenderItem::getViewsPipeline() {
if (!_drawAllViewsPipeline) {
auto vs = drawWorkloadView_vert::getShader();
auto ps = drawWorkloadView_frag::getShader();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding("workloadViewsBuffer", 1));
slotBindings.insert(gpu::Shader::Binding("drawMeshBuffer", 0));
gpu::Shader::makeProgram(*program, slotBindings);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL);
/* state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);*/
PrepareStencil::testMaskDrawShape(*state);
state->setCullMode(gpu::State::CULL_NONE);
_drawAllViewsPipeline = gpu::Pipeline::create(program, state);
}
return _drawAllViewsPipeline;
}
const gpu::BufferPointer GameWorkloadRenderItem::getDrawViewBuffer() {
if (!_drawViewBuffer) {
int numSegments = 64;
float angleStep = (float)M_PI * 2.0f / (float)numSegments;
struct Vert {
glm::vec4 p;
};
std::vector<Vert> verts(numSegments + 1);
for (int i = 0; i < numSegments; i++) {
float angle = (float)i * angleStep;
verts[i].p.x = cos(angle);
verts[i].p.y = sin(angle);
verts[i].p.z = angle;
verts[i].p.w = 1.0f;
}
verts[numSegments] = verts[0];
verts[numSegments].p.w = 0.0f;
_drawViewBuffer = std::make_shared<gpu::Buffer>(verts.size() * sizeof(Vert), (const gpu::Byte*) verts.data());
_numDrawViewVerts = numSegments + 1;
}
return _drawViewBuffer;
}
void GameWorkloadRenderItem::render(RenderArgs* args) {
gpu::Batch& batch = *(args->_batch);
batch.setModelTransform(Transform());
batch.setResourceBuffer(0, _allProxiesBuffer);
batch.setResourceBuffer(1, _allViewsBuffer);
// Show Proxies
if (_showProxies) {
batch.setPipeline(getProxiesPipeline());
static const int NUM_VERTICES_PER_PROXY = 3;
batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_PROXY * _numAllProxies, 0);
}
// Show Views
if (_showViews) {
batch.setPipeline(getViewsPipeline());
batch.setUniformBuffer(0, getDrawViewBuffer());
static const int NUM_VERTICES_PER_DRAWVIEWVERT = 2;
static const int NUM_REGIONS = 3;
batch.draw(gpu::TRIANGLE_STRIP, NUM_REGIONS * NUM_VERTICES_PER_DRAWVIEWVERT * _numDrawViewVerts * _numAllViews, 0);
}
batch.setResourceBuffer(0, nullptr);
batch.setResourceBuffer(1, nullptr);
}

View file

@ -0,0 +1,104 @@
//
// GameWorkloadRender.h
//
// Created by Sam Gateau on 2/20/2018.
// Copyright 2018 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_GameWorkloadRenderer_h
#define hifi_GameWorkloadRenderer_h
#include "GameWorkload.h"
class GameSpaceToRenderConfig : public workload::Job::Config {
Q_OBJECT
Q_PROPERTY(bool freezeViews MEMBER freezeViews NOTIFY dirty)
Q_PROPERTY(bool showProxies MEMBER showProxies NOTIFY dirty)
Q_PROPERTY(bool showViews MEMBER showViews NOTIFY dirty)
public:
bool freezeViews{ false };
bool showProxies{ false };
bool showViews{ false };
signals:
void dirty();
protected:
};
class GameSpaceToRender {
public:
using Config = GameSpaceToRenderConfig;
using Outputs = render::Transaction;
using JobModel = workload::Job::ModelO<GameSpaceToRender, Outputs, Config>;
GameSpaceToRender() {}
void configure(const Config& config);
void run(const workload::WorkloadContextPointer& renderContext, Outputs& outputs);
protected:
render::ItemID _spaceRenderItemID{ render::Item::INVALID_ITEM_ID };
bool _freezeViews{ false };
bool _showAllProxies{ false };
bool _showAllViews{ false };
};
class GameWorkloadRenderItem {
public:
using Payload = render::Payload<GameWorkloadRenderItem>;
using Pointer = Payload::DataPointer;
GameWorkloadRenderItem();
~GameWorkloadRenderItem() {}
void render(RenderArgs* args);
render::Item::Bound& editBound() { return _bound; }
const render::Item::Bound& getBound() { return _bound; }
void setVisible(bool visible);
void showProxies(bool show);
void showViews(bool show);
void setAllProxies(const workload::Proxy::Vector& proxies);
void setAllViews(const workload::Views& views);
render::ItemKey getKey() const;
protected:
render::Item::Bound _bound;
workload::Proxy::Vector _myOwnProxies;
gpu::BufferPointer _allProxiesBuffer;
uint32_t _numAllProxies{ 0 };
workload::Views _myOwnViews;
gpu::BufferPointer _allViewsBuffer;
uint32_t _numAllViews{ 0 };
gpu::PipelinePointer _drawAllProxiesPipeline;
const gpu::PipelinePointer getProxiesPipeline();
gpu::PipelinePointer _drawAllViewsPipeline;
const gpu::PipelinePointer getViewsPipeline();
uint32_t _numDrawViewVerts{ 0 };
gpu::BufferPointer _drawViewBuffer;
const gpu::BufferPointer getDrawViewBuffer();
render::ItemKey _key;
bool _showProxies{ true };
bool _showViews{ true };
};
namespace render {
template <> const ItemKey payloadGetKey(const GameWorkloadRenderItem::Pointer& payload);
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload);
template <> void payloadRender(const GameWorkloadRenderItem::Pointer& payload, RenderArgs* args);
template <> const ShapeKey shapeGetShapeKey(const GameWorkloadRenderItem::Pointer& payload);
}
#endif

View file

@ -0,0 +1,33 @@
//
// PhysicsBoundary.h
//
// Created by Andrew Meadows 2018.04.05
// Copyright 2018 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 "PhysicsBoundary.h"
#include <PhysicsLogging.h>
#include <workload/Space.h>
#include "workload/GameWorkload.h"
void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const Inputs& inputs) {
auto space = context->_space;
if (!space) {
return;
}
GameWorkloadContext* gameContext = static_cast<GameWorkloadContext*>(context.get());
PhysicalEntitySimulationPointer simulation = gameContext->_simulation;
const auto& regionChanges = inputs.get0();
for (uint32_t i = 0; i < (uint32_t)regionChanges.size(); ++i) {
const workload::Space::Change& change = regionChanges[i];
auto entity = space->getOwner(change.proxyId).get<EntityItemPointer>();
if (entity) {
simulation->changeEntity(entity);
}
}
}

View file

@ -0,0 +1,31 @@
//
// PhysicsBoundary.h
//
// Created by Andrew Meadows 2018.04.05
// Copyright 2018 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_PhysicsGatekeeper_h
#define hifi_PhysicsGatekeeper_h
#include <EntityItem.h>
#include <workload/Engine.h>
#include <workload/RegionTracker.h>
#include "PhysicalEntitySimulation.h"
class PhysicsBoundary {
public:
using Config = workload::Job::Config;
using Inputs = workload::RegionTracker::Outputs;
using Outputs = bool;
using JobModel = workload::Job::ModelI<PhysicsBoundary, Inputs, Config>; // this doesn't work
PhysicsBoundary() {}
void configure(const Config& config) { }
void run(const workload::WorkloadContextPointer& context, const Inputs& inputs);
};
#endif // hifi_PhysicsGatekeeper_h

View file

@ -105,8 +105,10 @@ QStringList Animation::getJointNames() const {
return result;
}
QStringList names;
foreach (const FBXJoint& joint, _geometry->joints) {
names.append(joint.name);
if (_geometry) {
foreach (const FBXJoint& joint, _geometry->joints) {
names.append(joint.name);
}
}
return names;
}
@ -114,11 +116,15 @@ QStringList Animation::getJointNames() const {
QVector<FBXAnimationFrame> Animation::getFrames() const {
if (QThread::currentThread() != thread()) {
QVector<FBXAnimationFrame> result;
BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getFrames",
BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getFrames",
Q_RETURN_ARG(QVector<FBXAnimationFrame>, result));
return result;
}
return _geometry->animationFrames;
if (_geometry) {
return _geometry->animationFrames;
} else {
return QVector<FBXAnimationFrame>();
}
}
const QVector<FBXAnimationFrame>& Animation::getFramesReference() const {

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

@ -14,6 +14,7 @@ include_hifi_library_headers(audio)
include_hifi_library_headers(entities)
include_hifi_library_headers(octree)
include_hifi_library_headers(task)
include_hifi_library_headers(workload)
include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h
target_bullet()

View file

@ -49,6 +49,9 @@ const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
const float DISPLAYNAME_ALPHA = 1.0f;
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
const float Avatar::MYAVATAR_LOADING_PRIORITY = (float)M_PI; // Entity priority is computed as atan2(maxDim, distance) which is <= PI / 2
const float Avatar::OTHERAVATAR_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON;
const float Avatar::ATTACHMENT_LOADING_PRIORITY = OTHERAVATAR_LOADING_PRIORITY - EPSILON;
namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
@ -304,7 +307,6 @@ void Avatar::updateAvatarEntities() {
// NOTE: if this avatar entity is not attached to us, strip its entity script completely...
auto attachedScript = properties.getScript();
if (!isMyAvatar() && !attachedScript.isEmpty()) {
qCDebug(avatars_renderer) << "removing entity script from avatar attached entity:" << entityID << "old script:" << attachedScript;
QString noScript;
properties.setScript(noScript);
}
@ -551,10 +553,17 @@ void Avatar::measureMotionDerivatives(float deltaTime) {
// angular
glm::quat orientation = getWorldOrientation();
glm::quat delta = glm::inverse(_lastOrientation) * orientation;
glm::vec3 angularVelocity = glm::axis(delta) * glm::angle(delta) * invDeltaTime;
setWorldAngularVelocity(angularVelocity);
_lastOrientation = getWorldOrientation();
float changeDot = glm::abs(glm::dot(orientation, _lastOrientation));
float CHANGE_DOT_THRESHOLD = 0.9999f;
if (changeDot < CHANGE_DOT_THRESHOLD) {
float angle = 2.0f * acosf(changeDot);
glm::quat delta = glm::inverse(_lastOrientation) * orientation;
glm::vec3 angularVelocity = (angle * invDeltaTime) * glm::axis(delta);
setWorldAngularVelocity(angularVelocity);
_lastOrientation = orientation;
} else {
setWorldAngularVelocity(glm::vec3(0.0f));
}
}
enum TextRendererType {
@ -861,7 +870,6 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
return true;
}
// virtual
void Avatar::simulateAttachments(float deltaTime) {
assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size());
PerformanceTimer perfTimer("attachments");
@ -1330,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);
@ -1419,6 +1430,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (_attachmentModels[i]->getURL() != attachmentData[i].modelURL) {
_attachmentModelsTexturesLoaded[i] = false;
}
_attachmentModels[i]->setLoadingPriority(ATTACHMENT_LOADING_PRIORITY);
_attachmentModels[i]->setURL(attachmentData[i].modelURL);
}
}
@ -1544,12 +1556,13 @@ void Avatar::updateDisplayNameAlpha(bool showDisplayName) {
}
}
// virtual
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
float uniformScale = getModelScale();
shapeInfo.setCapsuleY(uniformScale * _skeletonModel->getBoundingCapsuleRadius(),
0.5f * uniformScale * _skeletonModel->getBoundingCapsuleHeight());
shapeInfo.setOffset(uniformScale * _skeletonModel->getBoundingCapsuleOffset());
float radius = glm::max(MIN_AVATAR_RADIUS, uniformScale * _skeletonModel->getBoundingCapsuleRadius());
float height = glm::max(MIN_AVATAR_HEIGHT, uniformScale * _skeletonModel->getBoundingCapsuleHeight());
shapeInfo.setCapsuleY(radius, 0.5f * height);
glm::vec3 offset = uniformScale * _skeletonModel->getBoundingCapsuleOffset();
shapeInfo.setOffset(offset);
}
void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
@ -1572,9 +1585,8 @@ float Avatar::computeMass() {
return _density * TWO_PI * radius * radius * (glm::length(end - start) + 2.0f * radius / 3.0f);
}
// virtual
void Avatar::rebuildCollisionShape() {
addPhysicsFlags(Simulation::DIRTY_SHAPE);
addPhysicsFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
}
void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) {

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()); }
@ -540,6 +536,10 @@ protected:
AABox _renderBound;
bool _isMeshVisible{ true };
bool _needMeshVisibleSwitch{ true };
static const float MYAVATAR_LOADING_PRIORITY;
static const float OTHERAVATAR_LOADING_PRIORITY;
static const float ATTACHMENT_LOADING_PRIORITY;
};
#endif // hifi_Avatar_h

View file

@ -1,18 +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);
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

@ -2400,8 +2400,8 @@ QVariant AttachmentData::toVariant() const {
QVariantMap result;
result["modelUrl"] = modelURL;
result["jointName"] = jointName;
result["translation"] = glmToQMap(translation);
result["rotation"] = glmToQMap(glm::degrees(safeEulerAngles(rotation)));
result["translation"] = vec3ToQMap(translation);
result["rotation"] = vec3ToQMap(glm::degrees(safeEulerAngles(rotation)));
result["scale"] = scale;
result["soft"] = isSoft;
return result;

View file

@ -83,6 +83,7 @@ public:
connect(qApp, &QCoreApplication::aboutToQuit, [this] {
shutdown();
});
setObjectName("Present");
}
~PresentThread() {

View file

@ -1,7 +1,7 @@
set(TARGET_NAME entities-renderer)
AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils)
setup_hifi_library(Network Script)
link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image qml ui pointers)
link_hifi_libraries(shared workload gpu procedural graphics model-networking script-engine render render-utils image qml ui pointers)
include_hifi_library_headers(networking)
include_hifi_library_headers(gl)
include_hifi_library_headers(ktx)

View file

@ -206,6 +206,7 @@ void EntityTreeRenderer::clear() {
}
// remove all entities from the scene
_space->clear();
auto scene = _viewState->getMain3DScene();
if (scene) {
render::Transaction transaction;
@ -290,6 +291,16 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
if (!entity->isParentPathComplete()) {
continue;
}
if (entity->getSpaceIndex() == -1) {
std::unique_lock<std::mutex> lock(_spaceLock);
auto spaceIndex = _space->allocateID();
workload::Sphere sphere(entity->getWorldPosition(), entity->getBoundingRadius());
workload::Transaction transaction;
transaction.reset(spaceIndex, sphere, workload::Owner(entity));
_space->enqueueTransaction(transaction);
entity->setSpaceIndex(spaceIndex);
connect(entity.get(), &EntityItem::spaceUpdate, this, &EntityTreeRenderer::handleSpaceUpdate, Qt::QueuedConnection);
}
auto entityID = entity->getEntityItemID();
processedIds.insert(entityID);
@ -299,7 +310,6 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
}
}
if (!processedIds.empty()) {
for (const auto& processedId : processedIds) {
_entitiesToAdd.erase(processedId);
@ -394,7 +404,6 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
uint64_t expiry = updateStart + timeBudget;
// process the sorted renderables
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr;
size_t numSorted = sortedRenderables.size();
while (!sortedRenderables.empty() && usecTimestampNow() < expiry) {
const auto renderable = sortedRenderables.top().getRenderer();
@ -419,10 +428,12 @@ void EntityTreeRenderer::update(bool simulate) {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
// here we update _currentFrame and _lastAnimated and sync with the server properties.
tree->update(simulate);
// Update the rendereable entities as needed
{
PerformanceTimer perfTimer("tree::update");
tree->update(simulate);
}
{ // Update the rendereable entities as needed
PROFILE_RANGE(simulation_physics, "Scene");
PerformanceTimer sceneTimer("scene");
auto scene = _viewState->getMain3DScene();
@ -434,6 +445,24 @@ void EntityTreeRenderer::update(bool simulate) {
scene->enqueueTransaction(transaction);
}
}
{
PerformanceTimer perfTimer("workload::transaction");
workload::Transaction spaceTransaction;
{ // update proxies in the workload::Space
std::unique_lock<std::mutex> lock(_spaceLock);
spaceTransaction.update(_spaceUpdates);
_spaceUpdates.clear();
}
{
std::vector<int32_t> staleProxies;
tree->swapStaleProxies(staleProxies);
spaceTransaction.remove(staleProxies);
{
std::unique_lock<std::mutex> lock(_spaceLock);
_space->enqueueTransaction(spaceTransaction);
}
}
}
if (simulate) {
// Handle enter/leave entity logic
@ -450,6 +479,11 @@ void EntityTreeRenderer::update(bool simulate) {
}
}
void EntityTreeRenderer::handleSpaceUpdate(std::pair<int32_t, glm::vec4> proxyUpdate) {
std::unique_lock<std::mutex> lock(_spaceLock);
_spaceUpdates.emplace_back(proxyUpdate.first, proxyUpdate.second);
}
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar) {
bool didUpdate = false;
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later

View file

@ -24,6 +24,7 @@
#include <TextureCache.h>
#include <OctreeProcessor.h>
#include <render/Forward.h>
#include <workload/Space.h>
class AbstractScriptingServicesInterface;
class AbstractViewStateInterface;
@ -116,6 +117,9 @@ public:
EntityItemPointer getEntity(const EntityItemID& id);
void onEntityChanged(const EntityItemID& id);
// Access the workload Space
workload::SpacePointer getWorkloadSpace() const { return _space; }
signals:
void enterEntity(const EntityItemID& entityItemID);
void leaveEntity(const EntityItemID& entityItemID);
@ -135,6 +139,8 @@ public slots:
EntityRendererPointer renderableForEntityId(const EntityItemID& id) const;
render::ItemID renderableIdForEntityId(const EntityItemID& id) const;
void handleSpaceUpdate(std::pair<int32_t, glm::vec4> proxyUpdate);
protected:
virtual OctreePointer createTree() override {
EntityTreePointer newTree = EntityTreePointer(new EntityTree(true));
@ -255,6 +261,10 @@ private:
static int _entitiesScriptEngineCount;
static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc;
static std::function<bool()> _entitiesShouldFadeFunction;
mutable std::mutex _spaceLock;
workload::SpacePointer _space{ new workload::Space() };
workload::Transaction::Updates _spaceUpdates;
};

View file

@ -284,8 +284,8 @@ bool EntityRenderer::addToScene(const ScenePointer& scene, Transaction& transact
makeStatusGetters(_entity, statusGetters);
renderPayload->addStatusGetters(statusGetters);
transaction.resetItem(_renderItemID, renderPayload);
updateInScene(scene, transaction);
onAddToScene(_entity);
updateInScene(scene, transaction);
return true;
}
@ -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

@ -188,7 +188,7 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const {
}
}
return model->needsReload();
return false;
}
void RenderableModelEntityItem::updateModelBounds() {
@ -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);
@ -1176,19 +1174,8 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
}
bool ModelEntityRenderer::needsRenderUpdate() const {
ModelPointer model;
withReadLock([&] {
model = _model;
});
if (model) {
if (_needsJointSimulation || _moving || _animating) {
return true;
}
// When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
// we will watch for that and ask the model to update it's render items
if (_parsedModelURL != model->getURL()) {
if (resultWithReadLock<bool>([&] {
if (_moving || _animating) {
return true;
}
@ -1196,16 +1183,37 @@ bool ModelEntityRenderer::needsRenderUpdate() const {
return true;
}
if (!_prevModelLoaded) {
return true;
}
return false;
})) {
return true;
}
ModelPointer model;
QUrl parsedModelURL;
withReadLock([&] {
model = _model;
parsedModelURL = _parsedModelURL;
});
if (model) {
// When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
// we will watch for that and ask the model to update it's render items
if (parsedModelURL != model->getURL()) {
return true;
}
if (model->needsReload()) {
return true;
}
// FIXME what is the difference between these two?
if (model->needsFixupInScene()) {
return true;
}
// FIXME what is the difference between these two? ^^^^
if (model->getRenderItemsNeedUpdate()) {
return true;
}
@ -1219,7 +1227,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
return true;
}
if (_lastModelURL != entity->getModelURL()) {
if (_parsedModelURL != entity->getModelURL()) {
return true;
}
@ -1232,10 +1240,6 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
return true;
}
if (_renderAnimationProperties != entity->getAnimationProperties()) {
return true;
}
if (_animating != entity->isAnimatingSomething()) {
return true;
}
@ -1249,7 +1253,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
});
if (model && model->isLoaded()) {
if (!entity->_dimensionsInitialized || entity->_needsInitialSimulation) {
if (!entity->_dimensionsInitialized || entity->_needsInitialSimulation || !entity->_originalTexturesRead) {
return true;
}
@ -1281,16 +1285,15 @@ void ModelEntityRenderer::setCollisionMeshKey(const void*key) {
void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
if (_hasModel != entity->hasModel()) {
_hasModel = entity->hasModel();
withWriteLock([&] {
_hasModel = entity->hasModel();
});
}
_marketplaceEntity = entity->getMarketplaceID().length() != 0;
_animating = entity->isAnimatingSomething();
withWriteLock([&] {
if (_lastModelURL != entity->getModelURL()) {
_lastModelURL = entity->getModelURL();
_parsedModelURL = QUrl(_lastModelURL);
_animating = entity->isAnimatingSomething();
if (_parsedModelURL != entity->getModelURL()) {
_parsedModelURL = QUrl(entity->getModelURL());
}
});
@ -1298,7 +1301,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
ModelPointer model;
withReadLock([&] { model = _model; });
if (!_hasModel) {
if ((bool)model) {
if (model) {
model->removeFromScene(scene, transaction);
withWriteLock([&] { _model.reset(); });
transaction.updateItem<PayloadProxyInterface>(getRenderItemID(), [](PayloadProxyInterface& data) {
@ -1312,8 +1315,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
}
// Check for addition
if (_hasModel && !(bool)_model) {
if (_hasModel && !model) {
model = std::make_shared<Model>(nullptr, entity.get());
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
connect(model.get(), &Model::setURLFinished, this, [&](bool didVisualGeometryRequestSucceed) {
setKey(didVisualGeometryRequestSucceed);
emit requestRenderUpdate();
@ -1323,26 +1327,34 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
}
_didLastVisualGeometryRequestSucceed = didVisualGeometryRequestSucceed;
});
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
entity->setModel(model);
withWriteLock([&] { _model = model; });
}
// From here on, we are guaranteed a populated model
withWriteLock([&] {
if (_parsedModelURL != model->getURL()) {
if (_parsedModelURL != model->getURL()) {
withWriteLock([&] {
_texturesLoaded = false;
model->setURL(_parsedModelURL);
}
});
});
}
// Nothing else to do unless the model is loaded
if (!model->isLoaded()) {
withWriteLock([&] {
_prevModelLoaded = false;
});
emit requestRenderUpdate();
return;
} else if (!_prevModelLoaded) {
withWriteLock([&] {
_prevModelLoaded = true;
});
}
// Check for initializing the model
// FIXME: There are several places below here where we are modifying the entity, which we should not be doing from the renderable
if (!entity->_dimensionsInitialized) {
EntityItemProperties properties;
properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it
@ -1360,16 +1372,16 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
// Default to _originalTextures to avoid remapping immediately and lagging on load
entity->_originalTextures = model->getTextures();
entity->_originalTexturesRead = true;
_currentTextures = entity->_originalTextures;
}
if (_lastTextures != entity->getTextures()) {
_texturesLoaded = false;
_lastTextures = entity->getTextures();
withWriteLock([&] {
_texturesLoaded = false;
_lastTextures = entity->getTextures();
});
auto newTextures = parseTexturesToMap(_lastTextures, entity->_originalTextures);
if (newTextures != _currentTextures) {
if (newTextures != model->getTextures()) {
model->setTextures(newTextures);
_currentTextures = newTextures;
}
}
if (entity->_needsJointSimulation) {
@ -1378,14 +1390,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
entity->updateModelBounds();
entity->stopModelOverrideIfNoParent();
render::hifi::Tag tagMask = getTagMask();
if (model->isVisible() != _visible) {
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
// the renderable item. As it stands now the model checks it's visible/invisible state
// so most of the time we don't do anything in this function.
model->setVisibleInScene(_visible, scene);
}
render::hifi::Tag tagMask = getTagMask();
if (model->getTagMask() != tagMask) {
model->setTagMask(tagMask, scene);
}
@ -1414,7 +1423,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
}
if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
_texturesLoaded = true;
withWriteLock([&] {
_texturesLoaded = true;
});
model->updateRenderItems();
} else if (!_texturesLoaded) {
emit requestRenderUpdate();
@ -1462,15 +1473,15 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
batch.setModelTransform(getModelTransform()); // we want to include the scale as well
DependencyManager::get<GeometryCache>()->renderWireCubeInstance(args, batch, greenColor);
// Enqueue updates for the next frame
#if WANT_EXTRA_DEBUGGING
// debugging...
gpu::Batch& batch = *args->_batch;
_model->renderDebugMeshBoxes(batch);
ModelPointer model;
withReadLock([&] {
model = _model;
});
if (model) {
model->renderDebugMeshBoxes(batch);
}
#endif
// Remap textures for the next frame to avoid flicker
// remapTextures();
}
void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames) {

View file

@ -170,7 +170,7 @@ protected:
private:
void animate(const TypedEntityPointer& entity);
void mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames);
bool jointsMapped() const { return _jointMappingURL == _renderAnimationProperties.getURL() && _jointMappingCompleted; }
bool jointsMapped() const { return _jointMappingCompleted; }
// Transparency is handled in ModelMeshPartPayload
virtual bool isTransparent() const override { return false; }
@ -179,32 +179,26 @@ private:
ModelPointer _model;
GeometryResource::Pointer _compoundShapeResource;
QString _lastTextures;
QVariantMap _currentTextures;
bool _texturesLoaded { false };
AnimationPropertyGroup _renderAnimationProperties;
int _lastKnownCurrentFrame { -1 };
#ifdef MODEL_ENTITY_USE_FADE_EFFECT
bool _hasTransitioned{ false };
#endif
bool _needsJointSimulation { false };
const void* _collisionMeshKey { nullptr };
// used on client side
bool _jointMappingCompleted{ false };
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
QString _jointMappingURL;
AnimationPointer _animation;
QString _lastModelURL;
QUrl _parsedModelURL;
bool _marketplaceEntity { false };
bool _shouldHighlight { false };
bool _animating { false };
uint64_t _lastAnimated { 0 };
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
bool _didLastVisualGeometryRequestSucceed { true };
bool _prevModelLoaded { false };
void processMaterials();
};

View file

@ -95,9 +95,6 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
auto newParticleProperties = entity->getParticleProperties();
if (!newParticleProperties.valid()) {
qCWarning(entitiesrenderer) << "Bad particle properties";
if (!entity->getParticleProperties().valid()) {
qCWarning(entitiesrenderer) << "Bad particle properties";
}
}
if (resultWithReadLock<bool>([&]{ return _particleProperties != newParticleProperties; })) {
@ -125,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) {
@ -189,7 +194,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
if (polarStart == 0.0f && polarFinish == 0.0f && emitDimensions.z == 0.0f) {
// Emit along z-axis from position
particle.velocity = (emitSpeed + 0.2f * speedSpread) * (emitOrientation * Vectors::UNIT_Z);
particle.velocity = (emitSpeed + randFloatInRange(-1.0f, 1.0f) * speedSpread) * (emitOrientation * Vectors::UNIT_Z);
particle.acceleration = emitAcceleration + randFloatInRange(-1.0f, 1.0f) * accelerationSpread;
} else {
@ -198,10 +203,9 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
// - Distribute points relatively evenly over ellipsoid surface
// - Distribute points relatively evenly within ellipsoid volume
float elevationMinZ = sin(PI_OVER_TWO - polarFinish);
float elevationMaxZ = sin(PI_OVER_TWO - polarStart);
// float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) *randFloat());
float elevationMinZ = sinf(PI_OVER_TWO - polarFinish);
float elevationMaxZ = sinf(PI_OVER_TWO - polarStart);
float elevation = asinf(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
float azimuth;
if (azimuthFinish >= azimuthStart) {
@ -309,7 +313,6 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
return;
}
// FIXME migrate simulation to a compute stage
stepSimulation();
@ -324,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

@ -45,9 +45,8 @@ private:
// CPU particles
// FIXME either switch to GPU compute particles or switch to simd updating of the particles
#if 1
struct CpuParticle {
float seed{ 0.0f };
float seed { 0.0f };
uint64_t expiration { 0 };
float lifetime { 0.0f };
glm::vec3 position;
@ -62,19 +61,6 @@ private:
}
};
using CpuParticles = std::deque<CpuParticle>;
#else
struct CpuParticles {
std::vector<float> seeds;
std::vector<float> lifetimes;
std::vector<vec4> positions;
std::vector<vec4> velocities;
std::vector<vec4> accelerations;
size_t size() const;
void resize(size_t size);
void integrate(float deltaTime);
};
#endif
template<typename T>

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

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