mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' of https://github.com/highfidelity/hifi into editHandleCollision
This commit is contained in:
commit
6a4c5ed12a
144 changed files with 4501 additions and 1322 deletions
|
@ -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=' + (project.hasProperty("BACKTRACE_URL") && project.hasProperty("BACKTRACE_TOKEN") ? 'ON' : 'OFF');
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
|
@ -46,6 +47,10 @@ android {
|
|||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
buildConfigField "String", "BACKTRACE_URL", "\"" + (project.hasProperty("BACKTRACE_URL") ? BACKTRACE_URL : '') + "\""
|
||||
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (project.hasProperty("BACKTRACE_TOKEN") ? 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", "\"" + (project.hasProperty("BACKTRACE_URL") ? BACKTRACE_URL : '') + "\""
|
||||
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (project.hasProperty("BACKTRACE_TOKEN") ? BACKTRACE_TOKEN : '') + "\""
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -149,6 +149,13 @@ def packages = [
|
|||
file: 'etc2comp-patched-armv8-libcpp.tgz',
|
||||
versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
|
||||
checksum: '14b02795d774457a33bbc60e00a786bc'
|
||||
],
|
||||
breakpad: [
|
||||
file: 'breakpad.zip',
|
||||
versionId: '2OwvCCZrF171wnte5T44AnjTYFhhJsGJ',
|
||||
checksum: 'a46062a3167dfedd4fb4916136e204d2',
|
||||
sharedLibFolder: 'lib',
|
||||
includeLibs: ['libbreakpad_client.a','libbreakpad.a']
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -367,6 +374,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 +386,7 @@ verifyDependencyDownloads.dependsOn verifyPolyvox
|
|||
verifyDependencyDownloads.dependsOn verifyTBB
|
||||
verifyDependencyDownloads.dependsOn verifyHifiAC
|
||||
verifyDependencyDownloads.dependsOn verifyEtc2Comp
|
||||
verifyDependencyDownloads.dependsOn verifyBreakpad
|
||||
|
||||
task extractDependencies(dependsOn: verifyDependencyDownloads) {
|
||||
doLast {
|
||||
|
@ -615,4 +624,4 @@ task testElf (dependsOn: 'externalNativeBuildDebug') {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
|
14
cmake/externals/etc2comp/CMakeLists.txt
vendored
14
cmake/externals/etc2comp/CMakeLists.txt
vendored
|
@ -40,16 +40,22 @@ if (WIN32)
|
|||
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_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/libEtcLib.a CACHE FILEPATH "Path to EtcLib debug library")
|
||||
|
||||
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}/libEtcLib.a 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
|
||||
|
|
22
cmake/macros/TargetBreakpad.cmake
Normal file
22
cmake/macros/TargetBreakpad.cmake
Normal 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()
|
||||
|
||||
|
|
@ -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})
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -253,8 +253,12 @@ Item {
|
|||
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;
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
@ -1096,6 +1098,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 +1163,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,9 +1226,6 @@ 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);
|
||||
|
@ -1366,6 +1348,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 +1755,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);
|
||||
|
@ -2534,6 +2539,8 @@ Application::~Application() {
|
|||
_main3DScene = nullptr;
|
||||
_renderEngine = nullptr;
|
||||
|
||||
_gameWorkload.shutdown();
|
||||
|
||||
DependencyManager::destroy<Preferences>();
|
||||
|
||||
_entityClipboard->eraseAllOctreeElements();
|
||||
|
@ -2990,6 +2997,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 +4537,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 +4936,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 +5290,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 +5627,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 +5654,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 +5668,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 +5676,7 @@ void Application::update(float deltaTime) {
|
|||
_physicsEngine->stepSimulation();
|
||||
});
|
||||
}
|
||||
auto t3 = std::chrono::high_resolution_clock::now();
|
||||
{
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
{
|
||||
|
@ -5698,12 +5722,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 +6203,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 +6595,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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
70
interface/src/CrashHandler_Breakpad.cpp
Normal file
70
interface/src/CrashHandler_Breakpad.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// 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>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool startCrashHandler() {
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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
|
27
interface/src/CrashHandler_None.cpp
Normal file
27
interface/src/CrashHandler_None.cpp
Normal 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
|
|
@ -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
|
33
interface/src/CrashRecoveryHandler.h
Normal file
33
interface/src/CrashRecoveryHandler.h
Normal 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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -145,3 +165,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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -64,6 +67,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 +81,7 @@ protected:
|
|||
virtual const btCollisionShape* computeNewShape() override;
|
||||
|
||||
AvatarSharedPointer _avatar;
|
||||
float _diameter { 0.0f };
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
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);
|
||||
accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, AccountManagerAuth::Required, QNetworkAccessManager::PostOperation,
|
||||
callbackParams, nullptr, multiPart);
|
||||
}
|
||||
|
||||
QString Snapshot::getSnapshotsLocation() {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
82
interface/src/workload/GameWorkload.cpp
Normal file
82
interface/src/workload/GameWorkload.cpp
Normal 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);
|
||||
}
|
45
interface/src/workload/GameWorkload.h
Normal file
45
interface/src/workload/GameWorkload.h
Normal 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
|
257
interface/src/workload/GameWorkloadRenderer.cpp
Normal file
257
interface/src/workload/GameWorkloadRenderer.cpp
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
104
interface/src/workload/GameWorkloadRenderer.h
Normal file
104
interface/src/workload/GameWorkloadRenderer.h
Normal 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
|
33
interface/src/workload/PhysicsBoundary.cpp
Normal file
33
interface/src/workload/PhysicsBoundary.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
31
interface/src/workload/PhysicsBoundary.h
Normal file
31
interface/src/workload/PhysicsBoundary.h
Normal 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
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
@ -551,10 +554,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 +871,6 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size());
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
|
@ -1419,6 +1428,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 +1554,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 +1583,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) {
|
||||
|
|
|
@ -540,6 +540,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
|
||||
|
|
|
@ -12,6 +12,7 @@ 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);
|
||||
|
|
|
@ -83,6 +83,7 @@ public:
|
|||
connect(qApp, &QCoreApplication::aboutToQuit, [this] {
|
||||
shutdown();
|
||||
});
|
||||
setObjectName("Present");
|
||||
}
|
||||
|
||||
~PresentThread() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const {
|
|||
}
|
||||
}
|
||||
|
||||
return model->needsReload();
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::updateModelBounds() {
|
||||
|
@ -1176,19 +1176,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 +1185,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 +1229,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_lastModelURL != entity->getModelURL()) {
|
||||
if (_parsedModelURL != entity->getModelURL()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1232,10 +1242,6 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_renderAnimationProperties != entity->getAnimationProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_animating != entity->isAnimatingSomething()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1249,7 +1255,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 +1287,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 +1303,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 +1317,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 +1329,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 +1374,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 +1392,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 +1425,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 +1475,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) {
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -95,7 +95,7 @@ bool DeleteEntityOperator::preRecursion(const OctreeElementPointer& element) {
|
|||
EntityItemPointer theEntity = details.entity;
|
||||
bool entityDeleted = entityTreeElement->removeEntityItem(theEntity, true); // remove it from the element
|
||||
assert(entityDeleted);
|
||||
(void)entityDeleted; // quite warning
|
||||
(void)entityDeleted; // quiet warning about unused variable
|
||||
_tree->clearEntityMapEntry(details.entity->getEntityItemID());
|
||||
_foundCount++;
|
||||
}
|
||||
|
|
|
@ -367,6 +367,10 @@ int EntityItem::expectedBytes() {
|
|||
return MINIMUM_HEADER_BYTES;
|
||||
}
|
||||
|
||||
const uint8_t PENDING_STATE_NOTHING = 0;
|
||||
const uint8_t PENDING_STATE_TAKE = 1;
|
||||
const uint8_t PENDING_STATE_RELEASE = 2;
|
||||
|
||||
// clients use this method to unpack FULL updates from entity-server
|
||||
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
||||
setSourceUUID(args.sourceUUID);
|
||||
|
@ -678,7 +682,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// setters to ignore what the server says.
|
||||
filterRejection = newSimOwner.getID().isNull();
|
||||
if (weOwnSimulation) {
|
||||
if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) {
|
||||
if (newSimOwner.getID().isNull() && !pendingRelease(lastEditedFromBufferAdjusted)) {
|
||||
// entity-server is trying to clear our ownership (probably at our own request)
|
||||
// but we actually want to own it, therefore we ignore this clear event
|
||||
// and pretend that we own it (e.g. we assume we'll receive ownership soon)
|
||||
|
@ -693,32 +697,53 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// recompute weOwnSimulation for later
|
||||
weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
||||
}
|
||||
} else if (_simulationOwner.pendingTake(now - maxPingRoundTrip)) {
|
||||
// we sent a bid already but maybe before this packet was sent from the server
|
||||
weOwnSimulation = true;
|
||||
} else if (_pendingOwnershipState == PENDING_STATE_TAKE) {
|
||||
// we're waiting to receive acceptance of a bid
|
||||
// this ownership data either satisifies our bid or does not
|
||||
bool bidIsSatisfied = newSimOwner.getID() == myNodeID &&
|
||||
(newSimOwner.getPriority() == _pendingOwnershipPriority ||
|
||||
(_pendingOwnershipPriority == VOLUNTEER_SIMULATION_PRIORITY &&
|
||||
newSimOwner.getPriority() == RECRUIT_SIMULATION_PRIORITY));
|
||||
|
||||
if (newSimOwner.getID().isNull()) {
|
||||
// the entity-server is trying to clear someone else's ownership
|
||||
// the entity-server is clearing someone else's ownership
|
||||
if (!_simulationOwner.isNull()) {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||
somethingChanged = true;
|
||||
_simulationOwner.clearCurrentOwner();
|
||||
}
|
||||
} else if (newSimOwner.getID() == myNodeID) {
|
||||
// the entity-server is awarding us ownership which is what we want
|
||||
_simulationOwner.set(newSimOwner);
|
||||
} else {
|
||||
if (newSimOwner.getID() != _simulationOwner.getID()) {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||
}
|
||||
if (_simulationOwner.set(newSimOwner)) {
|
||||
// the entity-server changed ownership
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (bidIsSatisfied || (somethingChanged && _pendingOwnershipTimestamp < now - maxPingRoundTrip)) {
|
||||
// the bid has been satisfied, or it has been invalidated by data sent AFTER the bid should have been received
|
||||
// in either case: accept our fate and clear pending state
|
||||
_pendingOwnershipState = PENDING_STATE_NOTHING;
|
||||
_pendingOwnershipPriority = 0;
|
||||
}
|
||||
weOwnSimulation = bidIsSatisfied || (_simulationOwner.getID() == myNodeID);
|
||||
} else {
|
||||
// we are not waiting to take ownership
|
||||
if (newSimOwner.getID() != _simulationOwner.getID()) {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||
}
|
||||
if (_simulationOwner.set(newSimOwner)) {
|
||||
// the entity-server changed ownership...
|
||||
somethingChanged = true;
|
||||
if (newSimOwner.getID() == myNodeID) {
|
||||
// we have recieved ownership
|
||||
weOwnSimulation = true;
|
||||
// accept our fate and clear pendingState (just in case)
|
||||
_pendingOwnershipState = PENDING_STATE_NOTHING;
|
||||
_pendingOwnershipPriority = 0;
|
||||
}
|
||||
}
|
||||
} else if (newSimOwner.matchesValidID(myNodeID) && !_simulationOwner.pendingTake(now)) {
|
||||
// entity-server tells us that we have simulation ownership while we never requested this for this EntityItem,
|
||||
// this could happen when the user reloads the cache and entity tree.
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||
somethingChanged = true;
|
||||
_simulationOwner.clearCurrentOwner();
|
||||
weOwnSimulation = false;
|
||||
} else if (_simulationOwner.set(newSimOwner)) {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||
somethingChanged = true;
|
||||
// recompute weOwnSimulation for later
|
||||
weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1333,18 +1358,39 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c
|
|||
properties._accelerationChanged = true;
|
||||
}
|
||||
|
||||
void EntityItem::flagForOwnershipBid(uint8_t priority) {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
|
||||
// we already own it
|
||||
_simulationOwner.promotePriority(priority);
|
||||
} else {
|
||||
// we don't own it yet
|
||||
_simulationOwner.setPendingPriority(priority, usecTimestampNow());
|
||||
void EntityItem::setScriptSimulationPriority(uint8_t priority) {
|
||||
uint8_t newPriority = stillHasGrabActions() ? glm::max(priority, SCRIPT_GRAB_SIMULATION_PRIORITY) : priority;
|
||||
if (newPriority != _scriptSimulationPriority) {
|
||||
// set the dirty flag to trigger a bid or ownership update
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
|
||||
_scriptSimulationPriority = newPriority;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::clearScriptSimulationPriority() {
|
||||
// DO NOT markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) here, because this
|
||||
// is only ever called from the code that actually handles the dirty flags, and it knows best.
|
||||
_scriptSimulationPriority = stillHasGrabActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : 0;
|
||||
}
|
||||
|
||||
void EntityItem::setPendingOwnershipPriority(uint8_t priority) {
|
||||
_pendingOwnershipTimestamp = usecTimestampNow();
|
||||
_pendingOwnershipPriority = priority;
|
||||
_pendingOwnershipState = (_pendingOwnershipPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
|
||||
}
|
||||
|
||||
bool EntityItem::pendingRelease(uint64_t timestamp) const {
|
||||
return _pendingOwnershipPriority == 0 &&
|
||||
_pendingOwnershipState == PENDING_STATE_RELEASE &&
|
||||
_pendingOwnershipTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
bool EntityItem::stillWaitingToTakeOwnership(uint64_t timestamp) const {
|
||||
return _pendingOwnershipPriority > 0 &&
|
||||
_pendingOwnershipState == PENDING_STATE_TAKE &&
|
||||
_pendingOwnershipTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
|
@ -1977,10 +2023,6 @@ void EntityItem::clearSimulationOwnership() {
|
|||
|
||||
}
|
||||
|
||||
void EntityItem::setPendingOwnershipPriority(uint8_t priority, const quint64& timestamp) {
|
||||
_simulationOwner.setPendingPriority(priority, timestamp);
|
||||
}
|
||||
|
||||
QString EntityItem::actionsToDebugString() {
|
||||
QString result;
|
||||
QVector<QByteArray> serializedActions;
|
||||
|
@ -2076,6 +2118,7 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a
|
|||
}
|
||||
|
||||
bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& actionID) {
|
||||
// TODO: some action
|
||||
bool success = false;
|
||||
withWriteLock([&] {
|
||||
checkWaitingToRemove(simulation);
|
||||
|
@ -2403,12 +2446,17 @@ void EntityItem::locationChanged(bool tellPhysics) {
|
|||
}
|
||||
}
|
||||
SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
|
||||
std::pair<int32_t, glm::vec4> data(_spaceIndex, glm::vec4(getWorldPosition(), _boundingRadius));
|
||||
emit spaceUpdate(data);
|
||||
somethingChangedNotification();
|
||||
}
|
||||
|
||||
void EntityItem::dimensionsChanged() {
|
||||
requiresRecalcBoxes();
|
||||
SpatiallyNestable::dimensionsChanged(); // Do what you have to do
|
||||
_boundingRadius = 0.5f * glm::length(getScaledDimensions());
|
||||
std::pair<int32_t, glm::vec4> data(_spaceIndex, glm::vec4(getWorldPosition(), _boundingRadius));
|
||||
emit spaceUpdate(data);
|
||||
somethingChangedNotification();
|
||||
}
|
||||
|
||||
|
@ -3012,6 +3060,11 @@ void EntityItem::retrieveMarketplacePublicKey() {
|
|||
});
|
||||
}
|
||||
|
||||
void EntityItem::setSpaceIndex(int32_t index) {
|
||||
assert(_spaceIndex == -1);
|
||||
_spaceIndex = index;
|
||||
}
|
||||
|
||||
void EntityItem::preDelete() {
|
||||
}
|
||||
|
||||
|
|
|
@ -311,14 +311,21 @@ public:
|
|||
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
|
||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||
void setSimulationOwner(const SimulationOwner& owner);
|
||||
void promoteSimulationPriority(uint8_t priority);
|
||||
|
||||
uint8_t getSimulationPriority() const { return _simulationOwner.getPriority(); }
|
||||
QUuid getSimulatorID() const { return _simulationOwner.getID(); }
|
||||
void clearSimulationOwnership();
|
||||
void setPendingOwnershipPriority(uint8_t priority, const quint64& timestamp);
|
||||
uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); }
|
||||
void rememberHasSimulationOwnershipBid() const;
|
||||
|
||||
// TODO: move this "ScriptSimulationPriority" and "PendingOwnership" stuff into EntityMotionState
|
||||
// but first would need to do some other cleanup. In the meantime these live here as "scratch space"
|
||||
// to allow libs that don't know about each other to communicate.
|
||||
void setScriptSimulationPriority(uint8_t priority);
|
||||
void clearScriptSimulationPriority();
|
||||
uint8_t getScriptSimulationPriority() const { return _scriptSimulationPriority; }
|
||||
void setPendingOwnershipPriority(uint8_t priority);
|
||||
uint8_t getPendingOwnershipPriority() const { return _pendingOwnershipPriority; }
|
||||
bool pendingRelease(uint64_t timestamp) const;
|
||||
bool stillWaitingToTakeOwnership(uint64_t timestamp) const;
|
||||
|
||||
// Certifiable Properties
|
||||
QString getItemName() const;
|
||||
|
@ -411,7 +418,6 @@ public:
|
|||
|
||||
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
||||
|
||||
void flagForOwnershipBid(uint8_t priority);
|
||||
void flagForMotionStateChange() { _flags |= Simulation::DIRTY_MOTION_TYPE; }
|
||||
|
||||
QString actionsToDebugString();
|
||||
|
@ -500,6 +506,10 @@ public:
|
|||
void setCauterized(bool value) { _cauterized = value; }
|
||||
bool getCauterized() const { return _cauterized; }
|
||||
|
||||
float getBoundingRadius() const { return _boundingRadius; }
|
||||
void setSpaceIndex(int32_t index);
|
||||
int32_t getSpaceIndex() const { return _spaceIndex; }
|
||||
|
||||
virtual void preDelete();
|
||||
virtual void postParentFixup() {}
|
||||
|
||||
|
@ -517,6 +527,7 @@ public:
|
|||
|
||||
signals:
|
||||
void requestRenderUpdate();
|
||||
void spaceUpdate(std::pair<int32_t, glm::vec4> data);
|
||||
|
||||
protected:
|
||||
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
||||
|
@ -668,6 +679,17 @@ protected:
|
|||
quint64 _lastUpdatedQueryAACubeTimestamp { 0 };
|
||||
uint64_t _simulationOwnershipExpiry { 0 };
|
||||
|
||||
float _boundingRadius { 0.0f };
|
||||
int32_t _spaceIndex { -1 }; // index to proxy in workload::Space
|
||||
|
||||
// TODO: move this "scriptSimulationPriority" and "pendingOwnership" stuff into EntityMotionState
|
||||
// but first would need to do some other cleanup. In the meantime these live here as "scratch space"
|
||||
// to allow libs that don't know about each other to communicate.
|
||||
uint64_t _pendingOwnershipTimestamp { 0 }; // timestamp of last owenership change request
|
||||
uint8_t _pendingOwnershipPriority { 0 }; // priority of last ownership change request
|
||||
uint8_t _pendingOwnershipState { 0 }; // TAKE or RELEASE
|
||||
uint8_t _scriptSimulationPriority { 0 }; // target priority based on script operations
|
||||
|
||||
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
|
||||
|
||||
bool _cloneable { ENTITY_ITEM_DEFAULT_CLONEABLE };
|
||||
|
|
|
@ -2423,9 +2423,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
if (appendState != OctreeElement::COMPLETED) {
|
||||
didntFitProperties = propertiesDidntFit;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
packetData->endSubTree();
|
||||
|
||||
const char* finalizedData = reinterpret_cast<const char*>(packetData->getFinalizedData());
|
||||
|
@ -2436,7 +2434,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
buffer.resize(finalizedSize);
|
||||
} else {
|
||||
qCDebug(entities) << "ERROR - encoded edit message doesn't fit in output buffer.";
|
||||
success = false;
|
||||
appendState = OctreeElement::NONE; // if we got here, then we didn't include the item
|
||||
// maybe we should assert!!!
|
||||
}
|
||||
|
@ -2444,7 +2441,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
packetData->discardSubTree();
|
||||
}
|
||||
|
||||
|
||||
return appendState;
|
||||
}
|
||||
|
||||
|
@ -2834,7 +2830,6 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
|
|||
outputLength = sizeof(numberOfIds);
|
||||
|
||||
memcpy(copyAt, entityItemID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
|
||||
copyAt += NUM_BYTES_RFC4122_UUID;
|
||||
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
buffer.resize(outputLength);
|
||||
|
@ -2856,7 +2851,6 @@ bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityID
|
|||
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
memcpy(copyAt, newEntityID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
|
||||
copyAt += NUM_BYTES_RFC4122_UUID;
|
||||
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
buffer.resize(outputLength);
|
||||
|
|
|
@ -339,7 +339,7 @@ public:
|
|||
void clearSimulationOwner();
|
||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||
void setSimulationOwner(const QByteArray& data);
|
||||
void promoteSimulationPriority(uint8_t priority) { _simulationOwner.promotePriority(priority); }
|
||||
void setSimulationPriority(uint8_t priority) { _simulationOwner.setPriority(priority); }
|
||||
|
||||
void setActionDataDirty() { _actionDataChanged = true; }
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properti
|
|||
|
||||
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);
|
||||
properties.setLastEdited(entity->getLastEdited());
|
||||
} else {
|
||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||
|
@ -494,7 +494,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
} else {
|
||||
// we make a bid for simulation ownership
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
}
|
||||
if (properties.queryAACubeRelatedPropertyChanged()) {
|
||||
|
@ -1363,7 +1363,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
}
|
||||
action->setIsMine(true);
|
||||
success = entity->addAction(simulation, action);
|
||||
entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
if (success) {
|
||||
|
@ -1379,7 +1379,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
|
|||
return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
bool success = entity->updateAction(simulation, actionID, arguments);
|
||||
if (success) {
|
||||
entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
}
|
||||
return success;
|
||||
});
|
||||
|
@ -1393,7 +1393,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid&
|
|||
success = entity->removeAction(simulation, actionID);
|
||||
if (success) {
|
||||
// reduce from grab to poke
|
||||
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
|
|
|
@ -31,6 +31,7 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) {
|
|||
void EntitySimulation::updateEntities() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
uint64_t now = usecTimestampNow();
|
||||
PerformanceTimer perfTimer("EntitySimulation::updateEntities");
|
||||
|
||||
// these methods may accumulate entries in _entitiesToBeDeleted
|
||||
expireMortalEntities(now);
|
||||
|
|
|
@ -97,6 +97,7 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
|||
if (_simulation) {
|
||||
_simulation->clearEntities();
|
||||
}
|
||||
_staleProxies.clear();
|
||||
QHash<EntityItemID, EntityItemPointer> localMap;
|
||||
localMap.swap(_entityMap);
|
||||
this->withWriteLock([&] {
|
||||
|
@ -276,10 +277,11 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
_isDirty = true;
|
||||
emit addingEntity(entity->getEntityItemID());
|
||||
|
||||
// find and hook up any entities with this entity as a (previously) missing parent
|
||||
fixupNeedsParentFixups();
|
||||
|
||||
emit addingEntity(entity->getEntityItemID());
|
||||
}
|
||||
|
||||
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||
|
@ -359,21 +361,34 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
// the sender is trying to take or continue ownership
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
// the sender is taking ownership
|
||||
properties.promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||
if (properties.getSimulationOwner().getPriority() == VOLUNTEER_SIMULATION_PRIORITY) {
|
||||
// the entity-server always promotes VOLUNTEER to RECRUIT to avoid ownership thrash
|
||||
// when dynamic objects first activate and multiple participants bid simultaneously
|
||||
properties.setSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||
}
|
||||
simulationBlocked = false;
|
||||
} else if (entity->getSimulatorID() == senderID) {
|
||||
// the sender is asserting ownership, maybe changing priority
|
||||
simulationBlocked = false;
|
||||
// the entity-server always promotes VOLUNTEER to RECRUIT to avoid ownership thrash
|
||||
// when dynamic objects first activate and multiple participants bid simultaneously
|
||||
if (properties.getSimulationOwner().getPriority() == VOLUNTEER_SIMULATION_PRIORITY) {
|
||||
properties.setSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||
}
|
||||
} else {
|
||||
// the sender is trying to steal ownership from another simulator
|
||||
// so we apply the rules for ownership change:
|
||||
// (1) higher priority wins
|
||||
// (2) equal priority wins if ownership filter has expired except...
|
||||
// (2) equal priority wins if ownership filter has expired
|
||||
// (3) VOLUNTEER priority is promoted to RECRUIT
|
||||
uint8_t oldPriority = entity->getSimulationPriority();
|
||||
uint8_t newPriority = properties.getSimulationOwner().getPriority();
|
||||
if (newPriority > oldPriority ||
|
||||
(newPriority == oldPriority && properties.getSimulationOwner().hasExpired())) {
|
||||
simulationBlocked = false;
|
||||
if (properties.getSimulationOwner().getPriority() == VOLUNTEER_SIMULATION_PRIORITY) {
|
||||
properties.setSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!simulationBlocked) {
|
||||
|
@ -391,6 +406,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
}
|
||||
if (simulationBlocked) {
|
||||
// squash ownership and physics-related changes.
|
||||
// TODO? replace these eight calls with just one?
|
||||
properties.setSimulationOwnerChanged(false);
|
||||
properties.setPositionChanged(false);
|
||||
properties.setRotationChanged(false);
|
||||
|
@ -729,6 +745,12 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
if (theEntity->isSimulated()) {
|
||||
_simulation->prepareEntityForDelete(theEntity);
|
||||
}
|
||||
|
||||
// keep a record of valid stale spaceIndices so they can be removed from the Space
|
||||
int32_t spaceIndex = theEntity->getSpaceIndex();
|
||||
if (spaceIndex != -1) {
|
||||
_staleProxies.push_back(spaceIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1853,6 +1875,7 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
|||
|
||||
void EntityTree::update(bool simulate) {
|
||||
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||
PerformanceTimer perfTimer("updateTree");
|
||||
withWriteLock([&] {
|
||||
fixupNeedsParentFixups();
|
||||
if (simulate && _simulation) {
|
||||
|
|
|
@ -274,6 +274,8 @@ public:
|
|||
|
||||
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
|
||||
|
||||
void swapStaleProxies(std::vector<int>& proxies) { proxies.swap(_staleProxies); }
|
||||
|
||||
void setIsServerlessMode(bool value) { _serverlessDomain = value; }
|
||||
bool isServerlessMode() const { return _serverlessDomain; }
|
||||
|
||||
|
@ -408,6 +410,8 @@ private:
|
|||
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToOverlayOperator;
|
||||
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromOverlayOperator;
|
||||
|
||||
std::vector<int32_t> _staleProxies;
|
||||
|
||||
bool _serverlessDomain { false };
|
||||
|
||||
std::map<QString, QString> _namedPaths;
|
||||
|
|
|
@ -53,20 +53,29 @@ void SimpleEntitySimulation::updateEntitiesInternal(uint64_t now) {
|
|||
}
|
||||
|
||||
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
if (!entity->getSimulatorID().isNull()) {
|
||||
if (entity->getDynamic()) {
|
||||
// we don't allow dynamic objects to move without an owner so nothing to do here
|
||||
} else if (entity->isMovingRelativeToParent()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.insert(entity);
|
||||
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
|
||||
} else if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesThatNeedSimulationOwner.insert(entity);
|
||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
|
||||
|
||||
if (entity->isMovingRelativeToParent()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,32 +86,50 @@ void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
||||
int numKinematicEntities = _simpleKinematicEntities.size();
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
if (numKinematicEntities != _simpleKinematicEntities.size()) {
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
uint32_t flags = entity->getDirtyFlags();
|
||||
if ((flags & Simulation::DIRTY_SIMULATOR_ID) || (flags & Simulation::DIRTY_VELOCITIES)) {
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.remove(entity);
|
||||
|
||||
if (entity->getDynamic()) {
|
||||
// we don't allow dynamic objects to move without an owner
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.erase(itr);
|
||||
}
|
||||
} else if (entity->isMovingRelativeToParent()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
} else {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.erase(itr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.insert(entity);
|
||||
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
|
||||
_entitiesThatNeedSimulationOwner.remove(entity);
|
||||
|
||||
if (entity->isMovingRelativeToParent()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
} else {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.erase(itr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.remove(entity);
|
||||
if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
||||
_entitiesThatNeedSimulationOwner.insert(entity);
|
||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
|
||||
}
|
||||
} else {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.insert(entity);
|
||||
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
|
||||
_entitiesThatNeedSimulationOwner.remove(entity);
|
||||
}
|
||||
entity->clearDirtyFlags();
|
||||
}
|
||||
|
||||
|
@ -131,6 +158,12 @@ void SimpleEntitySimulation::expireStaleOwnerships(uint64_t now) {
|
|||
uint64_t expiry = entity->getSimulationOwnershipExpiry();
|
||||
if (now > expiry) {
|
||||
itemItr = _entitiesWithSimulationOwner.erase(itemItr);
|
||||
if (entity->getDynamic()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
if (itr != _simpleKinematicEntities.end()) {
|
||||
_simpleKinematicEntities.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
// remove ownership and dirty all the tree elements that contain the it
|
||||
entity->clearSimulationOwnership();
|
||||
|
|
|
@ -16,29 +16,20 @@
|
|||
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
const uint8_t PENDING_STATE_NOTHING = 0;
|
||||
const uint8_t PENDING_STATE_TAKE = 1;
|
||||
const uint8_t PENDING_STATE_RELEASE = 2;
|
||||
|
||||
// static
|
||||
const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1;
|
||||
|
||||
SimulationOwner::SimulationOwner() :
|
||||
_id(),
|
||||
_expiry(0),
|
||||
_pendingBidTimestamp(0),
|
||||
_priority(0),
|
||||
_pendingBidPriority(0),
|
||||
_pendingState(PENDING_STATE_NOTHING)
|
||||
_priority(0)
|
||||
{
|
||||
}
|
||||
|
||||
SimulationOwner::SimulationOwner(const QUuid& id, uint8_t priority) :
|
||||
_id(id),
|
||||
_expiry(0),
|
||||
_pendingBidTimestamp(0),
|
||||
_priority(priority),
|
||||
_pendingBidPriority(0)
|
||||
_priority(priority)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -61,22 +52,13 @@ bool SimulationOwner::fromByteArray(const QByteArray& data) {
|
|||
void SimulationOwner::clear() {
|
||||
_id = QUuid();
|
||||
_expiry = 0;
|
||||
_pendingBidTimestamp = 0;
|
||||
_priority = 0;
|
||||
_pendingBidPriority = 0;
|
||||
_pendingState = PENDING_STATE_NOTHING;
|
||||
}
|
||||
|
||||
void SimulationOwner::setPriority(uint8_t priority) {
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
void SimulationOwner::promotePriority(uint8_t priority) {
|
||||
if (priority > _priority) {
|
||||
_priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
bool SimulationOwner::setID(const QUuid& id) {
|
||||
if (_id != id) {
|
||||
_id = id;
|
||||
|
@ -101,25 +83,11 @@ bool SimulationOwner::set(const SimulationOwner& owner) {
|
|||
return setID(owner._id) || oldPriority != _priority;
|
||||
}
|
||||
|
||||
void SimulationOwner::setPendingPriority(uint8_t priority, uint64_t timestamp) {
|
||||
_pendingBidPriority = priority;
|
||||
_pendingBidTimestamp = timestamp;
|
||||
_pendingState = (_pendingBidPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
|
||||
}
|
||||
|
||||
void SimulationOwner::updateExpiry() {
|
||||
const uint64_t OWNERSHIP_LOCKOUT_EXPIRY = 200 * USECS_PER_MSEC;
|
||||
_expiry = usecTimestampNow() + OWNERSHIP_LOCKOUT_EXPIRY;
|
||||
}
|
||||
|
||||
bool SimulationOwner::pendingRelease(uint64_t timestamp) {
|
||||
return _pendingBidPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingBidTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
bool SimulationOwner::pendingTake(uint64_t timestamp) {
|
||||
return _pendingBidPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingBidTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
void SimulationOwner::clearCurrentOwner() {
|
||||
_id = QUuid();
|
||||
_expiry = 0;
|
||||
|
|
|
@ -19,74 +19,75 @@
|
|||
#include <UUID.h>
|
||||
|
||||
// HighFidelity uses a distributed physics simulation where multiple "participants" simulate portions
|
||||
// of the same world. When portions overlap only one participant is allowed to be the authority for any
|
||||
// particular object. For a simulated entity the authoritative participant is called the simulation "owner" and
|
||||
// their duty is to send transform/velocity updates for the entity to the central entity-server.
|
||||
// The entity-server relays updates to other participants who apply them as "state synchronization"
|
||||
// to their own simulation.
|
||||
// of the same domain. Even when portions overlap only one participant is allowed to be the current
|
||||
// authority for any particular object's physical simulation. The authoritative participant is called the
|
||||
// "simulation owner" and its duty is to send "state synchronization" (transform/velocity) updates for an
|
||||
// entity to the entity-server. The entity-server relays updates to other participants who apply them to
|
||||
// their own simulation.
|
||||
//
|
||||
// Participants acquire ownership by sending a "bid" to the entity-server. The bid is a properties update:
|
||||
// {
|
||||
// "simulationOwner": { "ownerID" : sessionID, "priority" : priority },
|
||||
// { "simulationOwner": { "ownerID" : sessionID, "priority" : priority },
|
||||
// transform/velocity properties
|
||||
// }
|
||||
//
|
||||
// The entity-server is the authority as to who owns what and may reject a bid.
|
||||
// The rules for handling a bid are as follows:
|
||||
// The entity-server is the authority as to who owns what and may reject a bid. The rules for handling a
|
||||
// bid are as follows:
|
||||
//
|
||||
// (1) A bid may be refused for special ownership restrictions, but otherwise...
|
||||
//
|
||||
// (2) A bid at higher priority is accepted
|
||||
//
|
||||
// (3) A bid at equal priority is rejected if receieved within a grace-period (200msec)
|
||||
// of the last ownership transition, otherwise it is accepted
|
||||
// (3) A bid at equal priority is accepted unless it was received shortly after (within 200msec) of the
|
||||
// last ownership change. This to avoid rapid ownership transitions should multiple participants
|
||||
// bid simultaneously.
|
||||
//
|
||||
// (4) The current owner is the only participant allowed to clear ownership (entity-server can override).
|
||||
// (4) The current owner is the only participant allowed to clear their ownership or adjust priority.
|
||||
//
|
||||
// (5) The current owner is the only participant allowed to adjust priority (entity-server can override).
|
||||
//
|
||||
// (6) If an owner does not update the transform or velocities of an owned entity within some period
|
||||
// (5) If an owner does not update the transform or velocities of an owned entity within some period
|
||||
// (5 seconds) then ownership is cleared and the entity's velocities are zeroed. This to handle
|
||||
// the case when an owner drops off the network.
|
||||
//
|
||||
// The priority of a participant's bid depends on how "interested" it is in the entity's motion. The rules
|
||||
// for bidding are as follows:
|
||||
//
|
||||
// (7) A participant (almost) never assumes that a bid is accepted by the entity-server. It packs the
|
||||
// simulation owner and priority as if they really did change but doesn't actually modify them
|
||||
// locally. Thus, if the bid packet is lost the participant will re-send after some period.
|
||||
// The participant only updates its knowledge of who owns what when it recieves an update from the
|
||||
// entity-server. An exception is when the participant creates a moving entity: it assumes it starts
|
||||
// off owning any moving entities it creates.
|
||||
// (6) A participant (almost) never assumes its bid is accepted by the entity-server. It packs the
|
||||
// simulation owner properties as if they really did change but doesn't actually modify them
|
||||
// locally. Instead it waits to hear back from the entity-server for bid acceptance. If the entity
|
||||
// remains unowned the participant will resend the bid (assuming the bid pakcet was lost). The
|
||||
// exception is when the participant creates a moving entity: it assumes it starts off owning any
|
||||
// moving entities it creates.
|
||||
//
|
||||
// (8) When an unowned entity becomes active in the physics simulation the participant will
|
||||
// start a timer and if the entity is still unowned after some period (0.5 seconds)
|
||||
// it will bid at priority = VOLUNTEER (=2). The entity-server never grants ownership at VOLUNTEER
|
||||
// (7) When an entity becomes active in the physics simulation but is not owned the participant will
|
||||
// start a timer and if it is still unowned after expiry (0.5 seconds) the participant will
|
||||
// bid at priority = VOLUNTEER (=2). The entity-server never grants ownership at VOLUNTEER
|
||||
// priority: when a VOLUNTEER bid is accepted the entity-server always promotes the priority to
|
||||
// RECRUIT (VOLUNTEER + 1); this to avoid a race-condition which might rapidly transition ownership
|
||||
// RECRUIT (=VOLUNTEER + 1); this to avoid a race-condition which might rapidly transition ownership
|
||||
// when multiple participants (with variable ping-times to the server) bid simultaneously for a
|
||||
// recently activated entity.
|
||||
//
|
||||
// (9) When a participant changes an entity's transform/velocity it will bid at priority = POKE (=127)
|
||||
// (8) When a participant's script changes an entity's transform/velocity the participant will bid at
|
||||
// priority = POKE (=127)
|
||||
//
|
||||
// (10) When an entity touches MyAvatar the participant it will bid at priority = POKE.
|
||||
// (9) When an entity collides against MyAvatar the participant will bid at priority = POKE.
|
||||
//
|
||||
// (11) When a participant grabs an entity it will bid at priority = GRAB (=128).
|
||||
// (10) When a participant grabs an entity it will bid at priority = GRAB (=128). This to allow UserA
|
||||
// to whack UserB with a "sword" without losing ownership, since UserB will bid at POKE. If UserB
|
||||
// wants to contest for ownership they must also GRAB it.
|
||||
//
|
||||
// (12) When entityA, locally owned at priority = N, collides with an unowned entityB the owner will
|
||||
// also bid for entityB at priority = N-1 (or VOLUNTEER, whichever is larger).
|
||||
// (11) When EntityA, locally owned at priority = N, collides with an unowned EntityB the owner will
|
||||
// also bid for EntityB at priority = N-1 (or VOLUNTEER, whichever is larger).
|
||||
//
|
||||
// (13) When an entity comes to rest and is deactivated in the physics simulation the owner will
|
||||
// send an update to: clear their ownerhsip, set priority to zero, and set the object's
|
||||
// velocities to be zero. As per a normal bid, the owner does NOT assume that its ownership
|
||||
// has been cleared until it hears from the entity-server. This, if the packet is lost the
|
||||
// owner will re-send after some period.
|
||||
// (12) When an entity comes to rest and is deactivated in the physics simulation the owner will send
|
||||
// an update to: clear their ownerhsip, set priority to zero, and set the entity's velocities to
|
||||
// zero. As per a normal bid, the owner does NOT assume its ownership has been cleared until
|
||||
// it hears back from the entity-server. Thus, if the packet is lost the owner will re-send after
|
||||
// expiry.
|
||||
//
|
||||
// (14) When an entity's ownership priority drops below VOLUNTEER other participants may bid for it
|
||||
// immediately at priority = VOLUNTEER.
|
||||
// (13) When an entity is still active but the owner no longer wants to own it, the owner will drop its
|
||||
// priority to YIELD (=1, below VOLUNTEER) thereby signalling to other participants to bid for it.
|
||||
//
|
||||
// (15) When an entity is still active but the owner no longer wants to own it, it will drop its priority
|
||||
// to YIELD (=1, less than VOLUNTEER) thereby signalling to other participants to bid for it.
|
||||
// (14) When an entity's ownership priority drops to YIELD (=1, below VOLUNTEER) other participants may
|
||||
// bid for it immediately at VOLUNTEER.
|
||||
//
|
||||
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
||||
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
||||
|
@ -119,24 +120,18 @@ public:
|
|||
void clear();
|
||||
|
||||
void setPriority(uint8_t priority);
|
||||
void promotePriority(uint8_t priority);
|
||||
|
||||
// return true if id is changed
|
||||
bool setID(const QUuid& id);
|
||||
bool set(const QUuid& id, uint8_t priority);
|
||||
bool set(const SimulationOwner& owner);
|
||||
void setPendingPriority(uint8_t priority, uint64_t timestamp);
|
||||
|
||||
bool isNull() const { return _id.isNull(); }
|
||||
bool matchesValidID(const QUuid& id) const { return _id == id && !_id.isNull(); }
|
||||
|
||||
void updateExpiry();
|
||||
|
||||
bool hasExpired() const { return usecTimestampNow() > _expiry; }
|
||||
|
||||
uint8_t getPendingPriority() const { return _pendingBidPriority; }
|
||||
bool pendingRelease(uint64_t timestamp); // return true if valid pending RELEASE
|
||||
bool pendingTake(uint64_t timestamp); // return true if valid pending TAKE
|
||||
void clearCurrentOwner();
|
||||
|
||||
bool operator>=(uint8_t priority) const { return _priority >= priority; }
|
||||
|
@ -153,10 +148,7 @@ public:
|
|||
private:
|
||||
QUuid _id; // owner
|
||||
uint64_t _expiry; // time when ownership can transition at equal priority
|
||||
uint64_t _pendingBidTimestamp; // time when pending bid was set
|
||||
uint8_t _priority; // priority of current owner
|
||||
uint8_t _pendingBidPriority; // priority at which we'd like to own it
|
||||
uint8_t _pendingState; // NOTHING, TAKE, or RELEASE
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ int GL45Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, cons
|
|||
auto requestedBinding = slotBindings.find(info.name);
|
||||
if (requestedBinding != slotBindings.end()) {
|
||||
info.binding = (*requestedBinding)._location;
|
||||
glUniformBlockBinding(glprogram, info.index, info.binding);
|
||||
glShaderStorageBlockBinding(glprogram, info.index, info.binding);
|
||||
resourceBufferSlotMap[info.binding] = info.index;
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ int GL45Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, cons
|
|||
auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), GLBackend::isUnusedSlot);
|
||||
if (slotIt != resourceBufferSlotMap.end()) {
|
||||
info.binding = slotIt - resourceBufferSlotMap.begin();
|
||||
glUniformBlockBinding(glprogram, info.index, info.binding);
|
||||
glShaderStorageBlockBinding(glprogram, info.index, info.binding);
|
||||
} else {
|
||||
// This should never happen, an active ssbo cannot find an available slot among the max available?!
|
||||
info.binding = -1;
|
||||
|
|
|
@ -63,13 +63,13 @@ Sysmem& Sysmem::operator=(const Sysmem& sysmem) {
|
|||
|
||||
Sysmem::~Sysmem() {
|
||||
deallocateMemory( _data, _size );
|
||||
_data = NULL;
|
||||
_data = nullptr;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
Size Sysmem::allocate(Size size) {
|
||||
if (size != _size) {
|
||||
Byte* newData = NULL;
|
||||
Byte* newData = nullptr;
|
||||
Size newSize = 0;
|
||||
if (size > 0) {
|
||||
Size allocated = allocateMemory(&newData, size);
|
||||
|
@ -90,7 +90,7 @@ Size Sysmem::allocate(Size size) {
|
|||
|
||||
Size Sysmem::resize(Size size) {
|
||||
if (size != _size) {
|
||||
Byte* newData = NULL;
|
||||
Byte* newData = nullptr;
|
||||
Size newSize = 0;
|
||||
if (size > 0) {
|
||||
Size allocated = allocateMemory(&newData, size);
|
||||
|
@ -124,7 +124,7 @@ Size Sysmem::setData( Size size, const Byte* bytes ) {
|
|||
}
|
||||
|
||||
Size Sysmem::setSubData( Size offset, Size size, const Byte* bytes) {
|
||||
if (size && ((offset + size) <= getSize()) && bytes) {
|
||||
if (_data && size && ((offset + size) <= getSize()) && bytes) {
|
||||
memcpy( _data + offset, bytes, size );
|
||||
return size;
|
||||
}
|
||||
|
|
|
@ -140,7 +140,6 @@ struct IrradianceKTXPayload {
|
|||
data += sizeof(Version);
|
||||
|
||||
memcpy(&_irradianceSH, data, sizeof(SphericalHarmonics));
|
||||
data += sizeof(SphericalHarmonics);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -422,7 +422,7 @@ bool Geometry::areTexturesLoaded() const {
|
|||
}
|
||||
// Failed texture downloads need to be considered as 'loaded'
|
||||
// or the object will never fade in
|
||||
bool finished = texture->isLoaded() || texture->isFailed();
|
||||
bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined());
|
||||
if (!finished) {
|
||||
return true;
|
||||
}
|
||||
|
@ -434,8 +434,11 @@ bool Geometry::areTexturesLoaded() const {
|
|||
}
|
||||
|
||||
// If material textures are loaded, check the material translucency
|
||||
// FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
|
||||
// However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
|
||||
// Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
|
||||
const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
|
||||
if (albedoTexture.texture && albedoTexture.texture->getGPUTexture()) {
|
||||
if (albedoTexture.texture) {
|
||||
material->resetOpacityMap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,8 +267,6 @@ bool PacketSender::nonThreadedProcess() {
|
|||
// Keep average packets and time for "second half" of check interval
|
||||
_lastPPSCheck += (elapsedSinceLastCheck / 2);
|
||||
_packetsOverCheckInterval = (_packetsOverCheckInterval / 2);
|
||||
|
||||
elapsedSinceLastCheck = now - _lastPPSCheck;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,12 +294,10 @@ bool PacketSender::nonThreadedProcess() {
|
|||
DependencyManager::get<NodeList>()->sendPacketList(std::move(packetPair.second.second), *packetPair.first);
|
||||
}
|
||||
|
||||
|
||||
packetsSentThisCall += packetCount;
|
||||
_packetsOverCheckInterval += packetCount;
|
||||
_totalPacketsSent += packetCount;
|
||||
|
||||
|
||||
_totalBytesSent += packetSize;
|
||||
emit packetSent(packetSize); // FIXME should include number of packets?
|
||||
_lastSendTime = now;
|
||||
|
|
|
@ -706,7 +706,16 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
|
|||
}
|
||||
|
||||
void Resource::handleReplyFinished() {
|
||||
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
|
||||
if (!_request || _request != sender()) {
|
||||
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
|
||||
qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current"
|
||||
<< " request: " << sender() << ", " << _request;
|
||||
PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), {
|
||||
{ "from_cache", false },
|
||||
{ "size_mb", _bytesTotal / 1000000.0 }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), {
|
||||
{ "from_cache", _request->loadedFromCache() },
|
||||
|
@ -715,15 +724,8 @@ void Resource::handleReplyFinished() {
|
|||
|
||||
setSize(_bytesTotal);
|
||||
|
||||
if (!_request || _request != sender()) {
|
||||
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
|
||||
qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current"
|
||||
<< " request: " << sender() << ", " << _request;
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceCache::requestCompleted(_self);
|
||||
|
||||
|
||||
auto result = _request->getResult();
|
||||
if (result == ResourceRequest::Success) {
|
||||
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
|
||||
|
@ -733,7 +735,7 @@ void Resource::handleReplyFinished() {
|
|||
if (!relativePathURL.isEmpty()) {
|
||||
_effectiveBaseURL = relativePathURL;
|
||||
}
|
||||
|
||||
|
||||
auto data = _request->getData();
|
||||
emit loaded(data);
|
||||
downloadFinished(data);
|
||||
|
|
|
@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return static_cast<PacketVersion>(EntityVersion::CollisionMask16Bytes);
|
||||
return static_cast<PacketVersion>(EntityVersion::YieldSimulationOwnership);
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -236,7 +236,8 @@ enum class EntityVersion : PacketVersion {
|
|||
ShadowControl,
|
||||
MaterialData,
|
||||
CloneableData,
|
||||
CollisionMask16Bytes
|
||||
CollisionMask16Bytes,
|
||||
YieldSimulationOwnership
|
||||
};
|
||||
|
||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||
|
|
|
@ -401,7 +401,6 @@ int Octree::readElementData(const OctreeElementPointer& destinationElement, cons
|
|||
// tell the element to read the subsequent data
|
||||
int rootDataSize = _rootElement->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead, args);
|
||||
bytesRead += rootDataSize;
|
||||
bytesLeftToRead -= rootDataSize;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
set(TARGET_NAME physics)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared fbx entities graphics)
|
||||
link_hifi_libraries(shared task workload fbx entities graphics)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(avatars)
|
||||
|
|
|
@ -69,7 +69,7 @@ bool CharacterGhostObject::rayTest(const btVector3& start,
|
|||
const btVector3& end,
|
||||
CharacterRayResult& result) const {
|
||||
if (_world && _inWorld) {
|
||||
_world->rayTest(start, end, result);
|
||||
btGhostObject::rayTest(start, end, result);
|
||||
}
|
||||
return result.hasHit();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
class CharacterGhostShape;
|
||||
|
||||
class CharacterGhostObject : public btPairCachingGhostObject {
|
||||
class CharacterGhostObject : public btGhostObject {
|
||||
public:
|
||||
CharacterGhostObject() { }
|
||||
~CharacterGhostObject();
|
||||
|
|
|
@ -80,7 +80,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
||||
setShape(shape);
|
||||
|
||||
_bidPriority = _entity->getPendingOwnershipPriority();
|
||||
if (_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) {
|
||||
// client-only entities are always thus, so we cache this fact in _ownershipState
|
||||
_ownershipState = EntityMotionState::OwnershipState::Unownable;
|
||||
|
@ -140,28 +139,22 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
if (_entity->getSimulatorID().isNull()) {
|
||||
// simulation ownership has been removed
|
||||
if (glm::length2(_entity->getWorldVelocity()) == 0.0f) {
|
||||
// this object is coming to rest --> clear the ACTIVATION flag and _bidPriority
|
||||
// TODO: also check angularVelocity
|
||||
// this object is coming to rest
|
||||
flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_body->setActivationState(WANTS_DEACTIVATION);
|
||||
_bidPriority = 0;
|
||||
const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet
|
||||
_body->setDeactivationTime(ACTIVATION_EXPIRY);
|
||||
} else {
|
||||
// disowned object is still moving --> start timer for ownership bid
|
||||
// TODO? put a delay in here proportional to distance from object?
|
||||
upgradeBidPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
_bumpedPriority = glm::max(_bumpedPriority, VOLUNTEER_SIMULATION_PRIORITY);
|
||||
_nextBidExpiry = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
}
|
||||
_loopsWithoutOwner = 0;
|
||||
_numInactiveUpdates = 0;
|
||||
} else if (isLocallyOwned()) {
|
||||
// we just inherited ownership, make sure our desired priority matches what we have
|
||||
upgradeBidPriority(_entity->getSimulationPriority());
|
||||
} else {
|
||||
// the entity is owned by someone else, so we clear _bidPriority here
|
||||
// but _bidPriority may be updated to non-zero value if this object interacts with locally owned simulation
|
||||
// in which case we may try to bid again
|
||||
_bidPriority = 0;
|
||||
} else if (!isLocallyOwned()) {
|
||||
// the entity is owned by someone else
|
||||
_nextBidExpiry = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
_numInactiveUpdates = 0;
|
||||
}
|
||||
|
@ -170,9 +163,6 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
// The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bit means one of the following:
|
||||
// (1) we own it but may need to change the priority OR...
|
||||
// (2) we don't own it but should bid (because a local script has been changing physics properties)
|
||||
uint8_t newPriority = isLocallyOwned() ? _entity->getSimulationOwner().getPriority() : _entity->getSimulationOwner().getPendingPriority();
|
||||
upgradeBidPriority(newPriority);
|
||||
|
||||
// reset bid expiry so that we bid ASAP
|
||||
_nextBidExpiry = 0;
|
||||
}
|
||||
|
@ -298,7 +288,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
if (_entity->getSimulatorID().isNull()) {
|
||||
_loopsWithoutOwner++;
|
||||
if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextBidExpiry) {
|
||||
upgradeBidPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
_bumpedPriority = glm::max(_bumpedPriority, VOLUNTEER_SIMULATION_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,21 +315,23 @@ void EntityMotionState::setShape(const btCollisionShape* shape) {
|
|||
}
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
// NOTE: we only get here if we think we own the simulation
|
||||
// NOTE: this method is only ever called when the entity simulation is locally owned
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
||||
|
||||
// Since we own the simulation: make sure _bidPriority is not less than current owned priority
|
||||
// because: an _bidPriority of zero indicates that we should drop ownership when we have it.
|
||||
upgradeBidPriority(_entity->getSimulationPriority());
|
||||
// because: a _bidPriority of zero indicates that we should drop ownership in the send.
|
||||
// TODO: need to be able to detect when logic dictates we *decrease* priority
|
||||
// WIP: print info whenever _bidPriority mismatches what is known to the entity
|
||||
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parentTransformSuccess;
|
||||
Transform localToWorld = _entity->getParentTransform(parentTransformSuccess);
|
||||
Transform worldToLocal;
|
||||
Transform worldVelocityToLocal;
|
||||
if (parentTransformSuccess) {
|
||||
localToWorld.evalInverse(worldToLocal);
|
||||
worldVelocityToLocal = worldToLocal;
|
||||
worldVelocityToLocal.setTranslation(glm::vec3(0.0f));
|
||||
}
|
||||
|
||||
int numSteps = simulationStep - _lastStep;
|
||||
|
@ -347,7 +339,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
|
||||
if (_numInactiveUpdates > 0) {
|
||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
||||
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES || isServerlessMode()) {
|
||||
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
|
||||
// clear local ownership (stop sending updates) and let the server clear itself
|
||||
_entity->clearSimulationOwnership();
|
||||
return false;
|
||||
|
@ -388,12 +380,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
uint8_t priority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
upgradeBidPriority(priority);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
||||
// of remote EntitySimulation) and return true if the error is significant.
|
||||
|
||||
|
@ -438,6 +424,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
}
|
||||
|
||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||
// NOTE: this method is only ever called when the entity simulation is locally owned
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
||||
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||
|
@ -462,6 +449,7 @@ void EntityMotionState::updateSendVelocities() {
|
|||
if (!_body->isActive()) {
|
||||
// make sure all derivatives are zero
|
||||
clearObjectVelocities();
|
||||
// we pretend we sent the inactive update for this object
|
||||
_numInactiveUpdates = 1;
|
||||
} else {
|
||||
glm::vec3 gravity = _entity->getGravity();
|
||||
|
@ -526,10 +514,10 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
|
|||
properties.setLastEdited(now);
|
||||
|
||||
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||
uint8_t bidPriority = glm::max<uint8_t>(_bidPriority, VOLUNTEER_SIMULATION_PRIORITY);
|
||||
properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);
|
||||
// copy _bidPriority into pendingPriority...
|
||||
_entity->setPendingOwnershipPriority(_bidPriority, now);
|
||||
uint8_t finalBidPriority = computeFinalBidPriority();
|
||||
_entity->clearScriptSimulationPriority();
|
||||
properties.setSimulationOwner(Physics::getSessionUUID(), finalBidPriority);
|
||||
_entity->setPendingOwnershipPriority(finalBidPriority);
|
||||
|
||||
EntityTreeElementPointer element = _entity->getElement();
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
|
@ -548,10 +536,10 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
|
|||
_lastStep = step;
|
||||
_nextBidExpiry = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
|
||||
// finally: clear _bidPriority
|
||||
// which will may get promoted before next bid
|
||||
// or maybe we'll win simulation ownership
|
||||
_bidPriority = 0;
|
||||
// after sending a bid/update we clear _bumpedPriority
|
||||
// which might get promoted again next frame (after local script or simulation interaction)
|
||||
// or we might win the bid
|
||||
_bumpedPriority = 0;
|
||||
}
|
||||
|
||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||
|
@ -590,22 +578,30 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
properties.setLastEdited(now);
|
||||
_entity->setSimulationOwnershipExpiry(now + MAX_OUTGOING_SIMULATION_UPDATE_PERIOD);
|
||||
|
||||
if (_numInactiveUpdates > 0) {
|
||||
if (_numInactiveUpdates > 0 && _entity->getScriptSimulationPriority() == 0) {
|
||||
// the entity is stopped and inactive so we tell the server we're clearing simulatorID
|
||||
// but we remember we do still own it... and rely on the server to tell us we don't
|
||||
properties.clearSimulationOwner();
|
||||
_bidPriority = 0;
|
||||
_entity->setPendingOwnershipPriority(_bidPriority, now);
|
||||
} else if (_bidPriority != _entity->getSimulationPriority()) {
|
||||
// our desired priority has changed
|
||||
if (_bidPriority == 0) {
|
||||
// we should release ownership
|
||||
properties.clearSimulationOwner();
|
||||
} else {
|
||||
// we just need to change the priority
|
||||
properties.setSimulationOwner(Physics::getSessionUUID(), _bidPriority);
|
||||
_entity->setPendingOwnershipPriority(0);
|
||||
} else {
|
||||
uint8_t newPriority = computeFinalBidPriority();
|
||||
_entity->clearScriptSimulationPriority();
|
||||
// if we get here then we own the simulation and the object is NOT going inactive
|
||||
// if newPriority is zero, then it must be outside of R1, which means we should really set it to YIELD
|
||||
// which we achive by just setting it to the max of the two
|
||||
newPriority = glm::max(newPriority, YIELD_SIMULATION_PRIORITY);
|
||||
if (newPriority != _entity->getSimulationPriority() &&
|
||||
!(newPriority == VOLUNTEER_SIMULATION_PRIORITY && _entity->getSimulationPriority() == RECRUIT_SIMULATION_PRIORITY)) {
|
||||
// our desired priority has changed
|
||||
if (newPriority == 0) {
|
||||
// we should release ownership
|
||||
properties.clearSimulationOwner();
|
||||
} else {
|
||||
// we just need to inform the entity-server
|
||||
properties.setSimulationOwner(Physics::getSessionUUID(), newPriority);
|
||||
}
|
||||
_entity->setPendingOwnershipPriority(newPriority);
|
||||
}
|
||||
_entity->setPendingOwnershipPriority(_bidPriority, now);
|
||||
}
|
||||
|
||||
EntityItemID id(_entity->getID());
|
||||
|
@ -640,6 +636,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
});
|
||||
|
||||
_lastStep = step;
|
||||
|
||||
// after sending a bid/update we clear _bumpedPriority
|
||||
// which might get promoted again next frame (after local script or simulation interaction)
|
||||
// or we might win the bid
|
||||
_bumpedPriority = 0;
|
||||
}
|
||||
|
||||
uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
||||
|
@ -686,7 +687,7 @@ uint8_t EntityMotionState::getSimulationPriority() const {
|
|||
}
|
||||
|
||||
void EntityMotionState::slaveBidPriority() {
|
||||
upgradeBidPriority(_entity->getSimulationPriority());
|
||||
_bumpedPriority = glm::max(_bumpedPriority, _entity->getSimulationPriority());
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -697,7 +698,7 @@ QUuid EntityMotionState::getSimulatorID() const {
|
|||
|
||||
void EntityMotionState::bump(uint8_t priority) {
|
||||
assert(priority != 0);
|
||||
upgradeBidPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
_bumpedPriority = glm::max(_bumpedPriority, --priority);
|
||||
}
|
||||
|
||||
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||
|
@ -765,7 +766,6 @@ void EntityMotionState::setMotionType(PhysicsMotionType motionType) {
|
|||
resetMeasuredBodyAcceleration();
|
||||
}
|
||||
|
||||
|
||||
// virtual
|
||||
QString EntityMotionState::getName() const {
|
||||
assert(entityTreeIsLocked());
|
||||
|
@ -777,15 +777,15 @@ void EntityMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma
|
|||
_entity->computeCollisionGroupAndFinalMask(group, mask);
|
||||
}
|
||||
|
||||
bool EntityMotionState::shouldSendBid() {
|
||||
if (_bidPriority >= glm::max(_entity->getSimulationPriority(), VOLUNTEER_SIMULATION_PRIORITY)) {
|
||||
return true;
|
||||
} else {
|
||||
// NOTE: this 'else' case has a side-effect: it clears _bidPriority
|
||||
// which may be updated next simulation step (via collision or script event)
|
||||
_bidPriority = 0;
|
||||
return false;
|
||||
}
|
||||
bool EntityMotionState::shouldSendBid() const {
|
||||
// NOTE: this method is only ever called when the entity's simulation is NOT locally owned
|
||||
return _body->isActive() && (_region == workload::Region::R1) &&
|
||||
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority();
|
||||
}
|
||||
|
||||
uint8_t EntityMotionState::computeFinalBidPriority() const {
|
||||
return (_region == workload::Region::R1) ?
|
||||
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) : 0;
|
||||
}
|
||||
|
||||
bool EntityMotionState::isLocallyOwned() const {
|
||||
|
@ -793,8 +793,17 @@ bool EntityMotionState::isLocallyOwned() const {
|
|||
}
|
||||
|
||||
bool EntityMotionState::isLocallyOwnedOrShouldBe() const {
|
||||
return (_bidPriority > VOLUNTEER_SIMULATION_PRIORITY && _bidPriority > _entity->getSimulationPriority()) ||
|
||||
_entity->getSimulatorID() == Physics::getSessionUUID();
|
||||
// this method could also be called "shouldGenerateCollisionEventForLocalScripts()"
|
||||
// because that is the only reason it's used
|
||||
if (_entity->getSimulatorID() == Physics::getSessionUUID()) {
|
||||
return true;
|
||||
} else {
|
||||
return computeFinalBidPriority() > glm::max(VOLUNTEER_SIMULATION_PRIORITY, _entity->getSimulationPriority());
|
||||
}
|
||||
}
|
||||
|
||||
void EntityMotionState::setRegion(uint8_t region) {
|
||||
_region = region;
|
||||
}
|
||||
|
||||
void EntityMotionState::initForBid() {
|
||||
|
@ -807,10 +816,6 @@ void EntityMotionState::initForOwned() {
|
|||
_ownershipState = EntityMotionState::OwnershipState::LocallyOwned;
|
||||
}
|
||||
|
||||
void EntityMotionState::upgradeBidPriority(uint8_t priority) {
|
||||
_bidPriority = glm::max<uint8_t>(_bidPriority, priority);
|
||||
}
|
||||
|
||||
void EntityMotionState::clearObjectVelocities() const {
|
||||
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
|
||||
|
@ -829,9 +834,3 @@ void EntityMotionState::clearObjectVelocities() const {
|
|||
}
|
||||
_entity->setAcceleration(glm::vec3(0.0f));
|
||||
}
|
||||
|
||||
bool EntityMotionState::isServerlessMode() {
|
||||
EntityTreeElementPointer element = _entity->getElement();
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
return tree ? tree->isServerlessMode() : false;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <EntityTypes.h>
|
||||
#include <AACube.h>
|
||||
#include <workload/Region.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
|
||||
|
@ -86,13 +87,17 @@ public:
|
|||
|
||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
|
||||
bool shouldSendBid();
|
||||
bool shouldSendBid() const;
|
||||
uint8_t computeFinalBidPriority() const;
|
||||
|
||||
bool isLocallyOwned() const override;
|
||||
bool isLocallyOwnedOrShouldBe() const override; // aka shouldEmitCollisionEvents()
|
||||
|
||||
friend class PhysicalEntitySimulation;
|
||||
OwnershipState getOwnershipState() const { return _ownershipState; }
|
||||
|
||||
void setRegion(uint8_t region);
|
||||
|
||||
protected:
|
||||
void updateSendVelocities();
|
||||
uint64_t getNextBidExpiry() const { return _nextBidExpiry; }
|
||||
|
@ -102,11 +107,7 @@ protected:
|
|||
void updateServerPhysicsVariables();
|
||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||
|
||||
// changes _bidPriority only if priority is larger
|
||||
void upgradeBidPriority(uint8_t priority);
|
||||
|
||||
// upgradeBidPriority to value stored in _entity
|
||||
void slaveBidPriority();
|
||||
void slaveBidPriority(); // computeNewBidPriority() with value stored in _entity
|
||||
|
||||
void clearObjectVelocities() const;
|
||||
|
||||
|
@ -154,8 +155,8 @@ protected:
|
|||
uint8_t _loopsWithoutOwner;
|
||||
mutable uint8_t _accelerationNearlyGravityCount;
|
||||
uint8_t _numInactiveUpdates { 1 };
|
||||
uint8_t _bidPriority { 0 };
|
||||
bool _serverVariablesSet { false };
|
||||
uint8_t _bumpedPriority { 0 }; // the target simulation priority according to collision history
|
||||
uint8_t _region { workload::Region::INVALID };
|
||||
|
||||
bool isServerlessMode();
|
||||
};
|
||||
|
|
|
@ -92,7 +92,7 @@ void ObjectMotionState::setMass(float mass) {
|
|||
}
|
||||
|
||||
float ObjectMotionState::getMass() const {
|
||||
if (_shape) {
|
||||
if (_shape && _shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE) {
|
||||
// scale the density by the current Aabb volume to get mass
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
|
@ -348,8 +348,10 @@ void ObjectMotionState::updateLastKinematicStep() {
|
|||
|
||||
void ObjectMotionState::updateBodyMassProperties() {
|
||||
float mass = getMass();
|
||||
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||
_body->getCollisionShape()->calculateLocalInertia(mass, inertia);
|
||||
btVector3 inertia(1.0f, 1.0f, 1.0f);
|
||||
if (mass > 0.0f) {
|
||||
_body->getCollisionShape()->calculateLocalInertia(mass, inertia);
|
||||
}
|
||||
_body->setMassProps(mass, inertia);
|
||||
_body->updateInertiaTensor();
|
||||
}
|
||||
|
|
|
@ -110,8 +110,8 @@ public:
|
|||
MotionStateType getType() const { return _type; }
|
||||
virtual PhysicsMotionType getMotionType() const { return _motionType; }
|
||||
|
||||
void setMass(float mass);
|
||||
float getMass() const;
|
||||
virtual void setMass(float mass);
|
||||
virtual float getMass() const;
|
||||
|
||||
void setBodyLinearVelocity(const glm::vec3& velocity) const;
|
||||
void setBodyAngularVelocity(const glm::vec3& velocity) const;
|
||||
|
|
|
@ -48,12 +48,17 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
assert(!entity->isDead());
|
||||
if (entity->shouldBePhysical()) {
|
||||
uint8_t region = _space->getRegion(entity->getSpaceIndex());
|
||||
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
|
||||
bool canBeKinematic = region <= workload::Region::R3;
|
||||
if (shouldBePhysical) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (!motionState) {
|
||||
if (motionState) {
|
||||
motionState->setRegion(region);
|
||||
} else {
|
||||
_entitiesToAddToPhysics.insert(entity);
|
||||
}
|
||||
} else if (entity->isMovingRelativeToParent()) {
|
||||
} else if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
}
|
||||
|
@ -120,25 +125,43 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
uint8_t region = _space->getRegion(entity->getSpaceIndex());
|
||||
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
|
||||
bool canBeKinematic = region <= workload::Region::R3;
|
||||
if (motionState) {
|
||||
if (!entity->shouldBePhysical()) {
|
||||
// the entity should be removed from the physical simulation
|
||||
if (!shouldBePhysical) {
|
||||
if (motionState->isLocallyOwned()) {
|
||||
// zero velocities by first deactivating the RigidBody
|
||||
btRigidBody* body = motionState->getRigidBody();
|
||||
if (body) {
|
||||
body->forceActivationState(ISLAND_SLEEPING);
|
||||
motionState->updateSendVelocities(); // has side-effect of zeroing entity velocities for inactive body
|
||||
}
|
||||
|
||||
// send packet to remove ownership
|
||||
// NOTE: this packet will NOT be resent if lost, but the good news is:
|
||||
// the entity-server will eventually clear velocity and ownership for timeout
|
||||
motionState->sendUpdate(_entityPacketSender, _physicsEngine->getNumSubsteps());
|
||||
}
|
||||
|
||||
// remove from the physical simulation
|
||||
_incomingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
removeOwnershipData(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
if (entity->isMovingRelativeToParent()) {
|
||||
if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
} else {
|
||||
_incomingChanges.insert(motionState);
|
||||
}
|
||||
} else if (entity->shouldBePhysical()) {
|
||||
motionState->setRegion(region);
|
||||
} else if (shouldBePhysical) {
|
||||
// The intent is for this object to be in the PhysicsEngine, but it has no MotionState yet.
|
||||
// Perhaps it's shape has changed and it can now be added?
|
||||
_entitiesToAddToPhysics.insert(entity);
|
||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||
} else if (entity->isMovingRelativeToParent()) {
|
||||
} else if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
} else {
|
||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||
|
@ -187,18 +210,21 @@ const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhys
|
|||
for (auto entity: _entitiesToRemoveFromPhysics) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
assert(motionState);
|
||||
// TODO CLEan this, just a n extra check to avoid the crash that shouldn;t happen
|
||||
if (motionState) {
|
||||
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
if (entity->isDead() && entity->getElement()) {
|
||||
_deadEntities.insert(entity);
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
if (entity->isDead() && entity->getElement()) {
|
||||
_deadEntities.insert(entity);
|
||||
}
|
||||
|
||||
_incomingChanges.remove(motionState);
|
||||
removeOwnershipData(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
|
||||
// remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine)
|
||||
_objectsToDelete.push_back(motionState);
|
||||
}
|
||||
|
||||
_incomingChanges.remove(motionState);
|
||||
removeOwnershipData(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
|
||||
// remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine)
|
||||
_objectsToDelete.push_back(motionState);
|
||||
}
|
||||
_entitiesToRemoveFromPhysics.clear();
|
||||
return _objectsToDelete;
|
||||
|
@ -249,6 +275,9 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
|||
_physicalObjects.insert(motionState);
|
||||
result.push_back(motionState);
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
|
||||
// make sure the motionState's region is up-to-date before it is actually added to physics
|
||||
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
||||
} else {
|
||||
//qWarning() << "Failed to generate new shape for entity." << entity->getName();
|
||||
++entityItr;
|
||||
|
@ -277,12 +306,15 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result)
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) {
|
||||
bool serverlessMode = getEntityTree()->isServerlessMode();
|
||||
for (auto stateItr : motionStates) {
|
||||
ObjectMotionState* state = &(*stateItr);
|
||||
assert(state);
|
||||
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||
entityState->handleDeactivation();
|
||||
if (!serverlessMode) {
|
||||
entityState->handleDeactivation();
|
||||
}
|
||||
EntityItemPointer entity = entityState->getEntity();
|
||||
_entitiesToSort.insert(entity);
|
||||
}
|
||||
|
@ -328,13 +360,7 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
|
||||
if (getEntityTree()->isServerlessMode()) {
|
||||
EntityItemPointer entity = motionState->getEntity();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto sessionID = nodeList->getSessionUUID();
|
||||
entity->setSimulationOwner(SimulationOwner(sessionID, SCRIPT_GRAB_SIMULATION_PRIORITY));
|
||||
_owned.push_back(motionState);
|
||||
} else {
|
||||
if (!getEntityTree()->isServerlessMode()) {
|
||||
motionState->initForBid();
|
||||
motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
|
||||
_bids.push_back(motionState);
|
||||
|
@ -343,8 +369,10 @@ void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) {
|
||||
motionState->initForOwned();
|
||||
_owned.push_back(motionState);
|
||||
if (!getEntityTree()->isServerlessMode()) {
|
||||
motionState->initForOwned();
|
||||
_owned.push_back(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::sendOwnershipBids(uint32_t numSubsteps) {
|
||||
|
@ -383,6 +411,7 @@ void PhysicalEntitySimulation::sendOwnershipBids(uint32_t numSubsteps) {
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::sendOwnedUpdates(uint32_t numSubsteps) {
|
||||
bool serverlessMode = getEntityTree()->isServerlessMode();
|
||||
PROFILE_RANGE_EX(simulation_physics, "Update", 0x00000000, (uint64_t)_owned.size());
|
||||
uint32_t i = 0;
|
||||
while (i < _owned.size()) {
|
||||
|
@ -394,7 +423,7 @@ void PhysicalEntitySimulation::sendOwnedUpdates(uint32_t numSubsteps) {
|
|||
}
|
||||
_owned.remove(i);
|
||||
} else {
|
||||
if (_owned[i]->shouldSendUpdate(numSubsteps)) {
|
||||
if (!serverlessMode && _owned[i]->shouldSendUpdate(numSubsteps)) {
|
||||
_owned[i]->sendUpdate(_entityPacketSender, numSubsteps);
|
||||
}
|
||||
++i;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <EntityItem.h>
|
||||
#include <EntitySimulation.h>
|
||||
#include <workload/Space.h>
|
||||
|
||||
#include "PhysicsEngine.h"
|
||||
#include "EntityMotionState.h"
|
||||
|
@ -45,6 +46,7 @@ public:
|
|||
~PhysicalEntitySimulation();
|
||||
|
||||
void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender);
|
||||
void setWorkloadSpace(const workload::SpacePointer space) { _space = space; }
|
||||
|
||||
virtual void addDynamic(EntityDynamicPointer dynamic) override;
|
||||
virtual void applyDynamicChanges() override;
|
||||
|
@ -102,6 +104,7 @@ private:
|
|||
|
||||
VectorOfEntityMotionStates _owned;
|
||||
VectorOfEntityMotionStates _bids;
|
||||
workload::SpacePointer _space;
|
||||
uint64_t _nextBidExpiry;
|
||||
uint32_t _lastStepSendPackets { 0 };
|
||||
};
|
||||
|
|
|
@ -105,9 +105,9 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
}
|
||||
case MOTION_TYPE_DYNAMIC: {
|
||||
mass = motionState->getMass();
|
||||
if (mass != mass || mass < 1.0f) {
|
||||
qCDebug(physics) << "mass is too low, setting to 1.0 Kg --" << mass;
|
||||
mass = 1.0f;
|
||||
const float MIN_DYNAMIC_MASS = 0.01f;
|
||||
if (mass != mass || mass < MIN_DYNAMIC_MASS) {
|
||||
mass = MIN_DYNAMIC_MASS;
|
||||
}
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(motionState->getShape());
|
||||
assert(shape);
|
||||
|
|
|
@ -61,7 +61,6 @@ PointerFrameHeaderList parseFrameHeaders(uchar* const start, const size_t& size)
|
|||
current += sizeof(FrameSize);
|
||||
header.fileOffset = current - start;
|
||||
if (end - current < header.size) {
|
||||
current = end;
|
||||
break;
|
||||
}
|
||||
current += header.size;
|
||||
|
|
|
@ -286,6 +286,7 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const
|
|||
shaderParameters._size.y = size;
|
||||
}
|
||||
|
||||
auto primaryFramebuffer = inputs.get4();
|
||||
gpu::doInBatch("DrawHighlight::run", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(destinationFrameBuffer);
|
||||
|
@ -301,6 +302,9 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const
|
|||
batch.setResourceTexture(SCENE_DEPTH_MAP_SLOT, sceneDepthBuffer->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(HIGHLIGHTED_DEPTH_MAP_SLOT, highlightedDepthTexture);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
// Reset the framebuffer for overlay drawing
|
||||
batch.setFramebuffer(primaryFramebuffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -360,6 +364,7 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons
|
|||
RenderArgs* args = renderContext->args;
|
||||
const auto jitter = input.get2();
|
||||
|
||||
auto primaryFramebuffer = input.get3();
|
||||
gpu::doInBatch("DebugHighlight::run", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setFramebuffer(highlightRessources->getColorFramebuffer());
|
||||
|
@ -384,6 +389,9 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons
|
|||
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId);
|
||||
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
|
||||
// Reset the framebuffer for overlay drawing
|
||||
batch.setFramebuffer(primaryFramebuffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -532,12 +540,12 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren
|
|||
stream << "HighlightEffect" << i;
|
||||
name = stream.str();
|
||||
}
|
||||
const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect).asVarying();
|
||||
const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect, primaryFramebuffer).asVarying();
|
||||
task.addJob<DrawHighlight>(name, drawHighlightInputs, i, sharedParameters);
|
||||
}
|
||||
|
||||
// Debug highlight
|
||||
const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast<const render::Varying&>(highlight0Rect), jitter).asVarying();
|
||||
const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast<const render::Varying&>(highlight0Rect), jitter, primaryFramebuffer).asVarying();
|
||||
task.addJob<DebugHighlight>("HighlightDebug", debugInputs);
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ protected:
|
|||
class DrawHighlight {
|
||||
public:
|
||||
|
||||
using Inputs = render::VaryingSet4<DeferredFrameTransformPointer, HighlightRessourcesPointer, DeferredFramebufferPointer, glm::ivec4>;
|
||||
using Inputs = render::VaryingSet5<DeferredFrameTransformPointer, HighlightRessourcesPointer, DeferredFramebufferPointer, glm::ivec4, gpu::FramebufferPointer>;
|
||||
using Config = render::Job::Config;
|
||||
using JobModel = render::Job::ModelI<DrawHighlight, Inputs, Config>;
|
||||
|
||||
|
@ -182,7 +182,7 @@ signals:
|
|||
|
||||
class DebugHighlight {
|
||||
public:
|
||||
using Inputs = render::VaryingSet3<HighlightRessourcesPointer, glm::ivec4, glm::vec2>;
|
||||
using Inputs = render::VaryingSet4<HighlightRessourcesPointer, glm::ivec4, glm::vec2, gpu::FramebufferPointer>;
|
||||
using Config = DebugHighlightConfig;
|
||||
using JobModel = render::Job::ModelI<DebugHighlight, Inputs, Config>;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "RenderUtilsLogging.h"
|
||||
|
||||
using namespace render;
|
||||
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
||||
extern void initForwardPipelines(ShapePlumber& plumber);
|
||||
|
||||
void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) {
|
||||
timer = _gpuTimer;
|
||||
|
@ -35,7 +35,7 @@ void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, co
|
|||
DrawOverlay3D::DrawOverlay3D(bool opaque) :
|
||||
_shapePlumber(std::make_shared<ShapePlumber>()),
|
||||
_opaquePass(opaque) {
|
||||
initOverlay3DPipelines(*_shapePlumber);
|
||||
initForwardPipelines(*_shapePlumber);
|
||||
}
|
||||
|
||||
void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
|
@ -60,7 +60,7 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs&
|
|||
if (_opaquePass) {
|
||||
gpu::doInBatch("DrawOverlay3D::run::clear", args->_context, [&](gpu::Batch& batch){
|
||||
batch.enableStereo(false);
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false);
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTHSTENCIL, glm::vec4(), 1.f, 0, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
#include <sstream>
|
||||
|
||||
using namespace render;
|
||||
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
||||
extern void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
|
||||
RenderDeferredTask::RenderDeferredTask()
|
||||
|
|
|
@ -37,10 +37,7 @@
|
|||
#include "nop_frag.h"
|
||||
|
||||
using namespace render;
|
||||
extern void initForwardPipelines(ShapePlumber& plumber,
|
||||
const render::ShapePipeline::BatchSetter& batchSetter,
|
||||
const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
||||
extern void initForwardPipelines(ShapePlumber& plumber);
|
||||
|
||||
void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
|
||||
auto items = input.get<Input>();
|
||||
|
@ -48,8 +45,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
|
|||
|
||||
// Prepare the ShapePipelines
|
||||
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
|
||||
initForwardPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter());
|
||||
initOverlay3DPipelines(*shapePlumber);
|
||||
initForwardPipelines(*shapePlumber);
|
||||
|
||||
// Extract opaques / transparents / lights / metas / overlays / background
|
||||
const auto& opaques = items.get0()[RenderFetchCullSortTask::OPAQUE_SHAPE];
|
||||
|
|
|
@ -80,16 +80,6 @@
|
|||
#include "model_translucent_unlit_fade_frag.h"
|
||||
#include "model_translucent_normal_map_fade_frag.h"
|
||||
|
||||
#include "overlay3D_vert.h"
|
||||
#include "overlay3D_frag.h"
|
||||
#include "overlay3D_model_frag.h"
|
||||
#include "overlay3D_model_translucent_frag.h"
|
||||
#include "overlay3D_translucent_frag.h"
|
||||
#include "overlay3D_unlit_frag.h"
|
||||
#include "overlay3D_translucent_unlit_frag.h"
|
||||
#include "overlay3D_model_unlit_frag.h"
|
||||
#include "overlay3D_model_translucent_unlit_frag.h"
|
||||
|
||||
#include "model_shadow_vert.h"
|
||||
#include "skin_model_shadow_vert.h"
|
||||
#include "skin_model_shadow_dq_vert.h"
|
||||
|
@ -104,12 +94,16 @@
|
|||
#include "model_shadow_fade_frag.h"
|
||||
#include "skin_model_shadow_fade_frag.h"
|
||||
|
||||
#include "simple_vert.h"
|
||||
#include "forward_simple_textured_frag.h"
|
||||
#include "forward_simple_textured_transparent_frag.h"
|
||||
#include "forward_simple_textured_unlit_frag.h"
|
||||
|
||||
using namespace render;
|
||||
using namespace std::placeholders;
|
||||
|
||||
void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest = false);
|
||||
void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
void initForwardPipelines(ShapePlumber& plumber);
|
||||
void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
||||
|
||||
void addPlumberPipeline(ShapePlumber& plumber,
|
||||
|
@ -120,71 +114,6 @@ void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* a
|
|||
void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args);
|
||||
static bool forceLightBatchSetter{ false };
|
||||
|
||||
void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest) {
|
||||
auto vertex = overlay3D_vert::getShader();
|
||||
auto vertexModel = model_vert::getShader();
|
||||
auto pixel = overlay3D_frag::getShader();
|
||||
auto pixelTranslucent = overlay3D_translucent_frag::getShader();
|
||||
auto pixelUnlit = overlay3D_unlit_frag::getShader();
|
||||
auto pixelTranslucentUnlit = overlay3D_translucent_unlit_frag::getShader();
|
||||
auto pixelModel = overlay3D_model_frag::getShader();
|
||||
auto pixelModelTranslucent = overlay3D_model_translucent_frag::getShader();
|
||||
auto pixelModelUnlit = overlay3D_model_unlit_frag::getShader();
|
||||
auto pixelModelTranslucentUnlit = overlay3D_model_translucent_unlit_frag::getShader();
|
||||
|
||||
auto opaqueProgram = gpu::Shader::createProgram(vertex, pixel);
|
||||
auto translucentProgram = gpu::Shader::createProgram(vertex, pixelTranslucent);
|
||||
auto unlitOpaqueProgram = gpu::Shader::createProgram(vertex, pixelUnlit);
|
||||
auto unlitTranslucentProgram = gpu::Shader::createProgram(vertex, pixelTranslucentUnlit);
|
||||
auto materialOpaqueProgram = gpu::Shader::createProgram(vertexModel, pixelModel);
|
||||
auto materialTranslucentProgram = gpu::Shader::createProgram(vertexModel, pixelModelTranslucent);
|
||||
auto materialUnlitOpaqueProgram = gpu::Shader::createProgram(vertexModel, pixelModel);
|
||||
auto materialUnlitTranslucentProgram = gpu::Shader::createProgram(vertexModel, pixelModelTranslucent);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bool isCulled = (i & 1);
|
||||
bool isBiased = (i & 2);
|
||||
bool isOpaque = (i & 4);
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
if (depthTest) {
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
} else {
|
||||
state->setDepthTest(false);
|
||||
}
|
||||
state->setCullMode(isCulled ? gpu::State::CULL_BACK : gpu::State::CULL_NONE);
|
||||
if (isBiased) {
|
||||
state->setDepthBias(1.0f);
|
||||
state->setDepthBiasSlopeScale(1.0f);
|
||||
}
|
||||
if (isOpaque) {
|
||||
// Soft edges
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
} else {
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
}
|
||||
|
||||
ShapeKey::Filter::Builder builder;
|
||||
|
||||
isCulled ? builder.withCullFace() : builder.withoutCullFace();
|
||||
isBiased ? builder.withDepthBias() : builder.withoutDepthBias();
|
||||
isOpaque ? builder.withOpaque() : builder.withTranslucent();
|
||||
|
||||
auto simpleProgram = isOpaque ? opaqueProgram : translucentProgram;
|
||||
auto unlitProgram = isOpaque ? unlitOpaqueProgram : unlitTranslucentProgram;
|
||||
auto materialProgram = isOpaque ? materialOpaqueProgram : materialTranslucentProgram;
|
||||
auto materialUnlitProgram = isOpaque ? materialUnlitOpaqueProgram : materialUnlitTranslucentProgram;
|
||||
|
||||
plumber.addPipeline(builder.withMaterial().build().key(), materialProgram, state, &lightBatchSetter);
|
||||
plumber.addPipeline(builder.withMaterial().withUnlit().build().key(), materialUnlitProgram, state, &batchSetter);
|
||||
plumber.addPipeline(builder.withoutUnlit().withoutMaterial().build().key(), simpleProgram, state, &lightBatchSetter);
|
||||
plumber.addPipeline(builder.withUnlit().withoutMaterial().build().key(), unlitProgram, state, &batchSetter);
|
||||
}
|
||||
}
|
||||
|
||||
void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) {
|
||||
// Vertex shaders
|
||||
auto simpleVertex = simple_vert::getShader();
|
||||
|
@ -432,8 +361,9 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
|
|||
skinModelShadowFadeDualQuatVertex, modelShadowFadePixel, batchSetter, itemSetter);
|
||||
}
|
||||
|
||||
void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) {
|
||||
void initForwardPipelines(ShapePlumber& plumber) {
|
||||
// Vertex shaders
|
||||
auto simpleVertex = simple_vert::getShader();
|
||||
auto modelVertex = model_vert::getShader();
|
||||
auto modelNormalMapVertex = model_normal_map_vert::getShader();
|
||||
auto skinModelVertex = skin_model_vert::getShader();
|
||||
|
@ -443,6 +373,10 @@ void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::Ba
|
|||
auto skinModelNormalMapDualQuatVertex = skin_model_normal_map_dq_vert::getShader();
|
||||
|
||||
// Pixel shaders
|
||||
auto simplePixel = forward_simple_textured_frag::getShader();
|
||||
auto simpleTranslucentPixel = forward_simple_textured_transparent_frag::getShader();
|
||||
auto simpleUnlitPixel = forward_simple_textured_unlit_frag::getShader();
|
||||
auto simpleTranslucentUnlitPixel = simple_transparent_textured_unlit_frag::getShader();
|
||||
auto modelPixel = forward_model_frag::getShader();
|
||||
auto modelUnlitPixel = forward_model_unlit_frag::getShader();
|
||||
auto modelNormalMapPixel = forward_model_normal_map_frag::getShader();
|
||||
|
@ -458,8 +392,15 @@ void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::Ba
|
|||
};
|
||||
|
||||
// Forward pipelines need the lightBatchSetter for opaques and transparents
|
||||
// forceLightBatchSetter = true;
|
||||
forceLightBatchSetter = false;
|
||||
forceLightBatchSetter = true;
|
||||
|
||||
// Simple Opaques
|
||||
addPipeline(Key::Builder(), simpleVertex, simplePixel);
|
||||
addPipeline(Key::Builder().withUnlit(), simpleVertex, simpleUnlitPixel);
|
||||
|
||||
// Simple Translucents
|
||||
addPipeline(Key::Builder().withTranslucent(), simpleVertex, simpleTranslucentPixel);
|
||||
addPipeline(Key::Builder().withTranslucent().withUnlit(), simpleVertex, simpleTranslucentUnlitPixel);
|
||||
|
||||
// Opaques
|
||||
addPipeline(Key::Builder().withMaterial(), modelVertex, modelPixel);
|
||||
|
@ -501,7 +442,7 @@ void addPlumberPipeline(ShapePlumber& plumber,
|
|||
bool isWireframed = (i & 4);
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
key.isTranslucent() ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state);
|
||||
|
||||
// Depth test depends on transparency
|
||||
state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL);
|
||||
|
|
32
libraries/render-utils/src/drawWorkloadProxy.slf
Normal file
32
libraries/render-utils/src/drawWorkloadProxy.slf
Normal file
|
@ -0,0 +1,32 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// drawItemBounds.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 6/29/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include DeferredBufferWrite.slh@>
|
||||
<@include gpu/Paint.slh@>
|
||||
|
||||
in vec4 varColor;
|
||||
in vec3 varTexcoord;
|
||||
|
||||
void main(void) {
|
||||
if (varColor.w > 0.0) {
|
||||
float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz));
|
||||
float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w);
|
||||
if (a <= 0.1 || r > 1.1) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
||||
packDeferredFragmentUnlit(
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
1.0,
|
||||
varColor.rgb);
|
||||
}
|
94
libraries/render-utils/src/drawWorkloadProxy.slv
Normal file
94
libraries/render-utils/src/drawWorkloadProxy.slv
Normal file
|
@ -0,0 +1,94 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// drawItemBounds.slv
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Sam Gateau on 6/29/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
|
||||
//
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
<@include gpu/Color.slh@>
|
||||
<$declareColorWheel()$>
|
||||
|
||||
uniform vec4 inColor;
|
||||
|
||||
|
||||
struct WorkloadProxy {
|
||||
vec4 sphere;
|
||||
vec4 region;
|
||||
};
|
||||
|
||||
#if defined(GPU_GL410)
|
||||
uniform samplerBuffer workloadProxiesBuffer;
|
||||
WorkloadProxy getWorkloadProxy(int i) {
|
||||
int offset = 2 * i;
|
||||
WorkloadProxy proxy;
|
||||
proxy.sphere = texelFetch(workloadProxiesBuffer, offset);
|
||||
proxy.region = texelFetch(workloadProxiesBuffer, offset + 1);
|
||||
return proxy;
|
||||
}
|
||||
#else
|
||||
layout(std140) buffer workloadProxiesBuffer {
|
||||
WorkloadProxy _proxies[];
|
||||
};
|
||||
WorkloadProxy getWorkloadProxy(int i) {
|
||||
WorkloadProxy proxy = _proxies[i];
|
||||
return proxy;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
out vec4 varColor;
|
||||
out vec3 varTexcoord;
|
||||
|
||||
void main(void) {
|
||||
const vec4 UNIT_SPRITE[3] = vec4[3](
|
||||
vec4(-1.0, -1.0, 0.0, 1.0),
|
||||
vec4(3.0, -1.0, 0.0, 1.0),
|
||||
vec4(-1.0, 3.0, 0.0, 1.0)
|
||||
);
|
||||
const int UNIT_SPRITE_INDICES[3] = int[3](
|
||||
0, 1, 2
|
||||
);
|
||||
int proxyID = gl_VertexID / 3;
|
||||
int vertexID = gl_VertexID - proxyID * 3;
|
||||
|
||||
vec4 spriteVert = UNIT_SPRITE[UNIT_SPRITE_INDICES[vertexID]];
|
||||
|
||||
WorkloadProxy proxy = getWorkloadProxy(proxyID);
|
||||
vec4 proxyPosWorld = vec4(proxy.sphere.xyz, 1.0);
|
||||
|
||||
// standard transform, bring proxy in view space
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
vec4 proxyPosEye;
|
||||
<$transformModelToEyePos(cam, obj, proxyPosWorld, proxyPosEye)$>
|
||||
|
||||
// Define the billboarded space
|
||||
vec3 dirZ = -normalize(proxyPosEye.xyz);
|
||||
vec3 dirX = normalize(cross(vec3(0.0, 1.0, 0.0), dirZ));
|
||||
vec3 dirY = vec3(0.0, 1.0, 0.0);
|
||||
|
||||
vec4 pos = vec4(proxyPosEye.xyz + proxy.sphere.w * ( dirX * spriteVert.x + dirY * spriteVert.y /* + dirZ * spriteVert.z*/), 1.0);
|
||||
varTexcoord = spriteVert.xyz;
|
||||
<$transformEyeToClipPos(cam, pos, gl_Position)$>
|
||||
|
||||
// Convert region to color
|
||||
int region = floatBitsToInt(proxy.region.x);
|
||||
region = (0x000000FF & region);
|
||||
|
||||
varColor = vec4(colorWheel(float(region) / 4.0), proxy.sphere.w);
|
||||
|
||||
if (region == 4) {
|
||||
gl_Position = vec4(0.0);
|
||||
}
|
||||
}
|
32
libraries/render-utils/src/drawWorkloadView.slf
Normal file
32
libraries/render-utils/src/drawWorkloadView.slf
Normal file
|
@ -0,0 +1,32 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// drawItemBounds.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 6/29/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include DeferredBufferWrite.slh@>
|
||||
<@include gpu/Paint.slh@>
|
||||
|
||||
in vec4 varColor;
|
||||
in vec3 varTexcoord;
|
||||
|
||||
void main(void) {
|
||||
if (varColor.w > 0.0) {
|
||||
float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz));
|
||||
float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w);
|
||||
if (a <= 0.1 || r > 1.1) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
||||
packDeferredFragmentUnlit(
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
1.0,
|
||||
varColor.rgb);
|
||||
}
|
120
libraries/render-utils/src/drawWorkloadView.slv
Normal file
120
libraries/render-utils/src/drawWorkloadView.slv
Normal file
|
@ -0,0 +1,120 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// drawItemBounds.slv
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Sam Gateau on 6/29/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
|
||||
//
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
<@include gpu/Color.slh@>
|
||||
<$declareColorWheel()$>
|
||||
|
||||
uniform vec4 inColor;
|
||||
|
||||
|
||||
struct WorkloadView {
|
||||
vec4 direction_far;
|
||||
vec4 fov;
|
||||
vec4 origin;
|
||||
vec4 backFront[2];
|
||||
vec4 regions[3];
|
||||
};
|
||||
|
||||
#if defined(GPU_GL410)
|
||||
uniform samplerBuffer workloadViewsBuffer;
|
||||
WorkloadView getWorkloadView(int i) {
|
||||
int offset = 2 * i;
|
||||
WorkloadView view;
|
||||
view.origin = texelFetch(workloadViewsBuffer, offset);
|
||||
view.radiuses = texelFetch(workloadViewsBuffer, offset + 1);
|
||||
return view;
|
||||
}
|
||||
#else
|
||||
layout(std140) buffer workloadViewsBuffer {
|
||||
WorkloadView _views[];
|
||||
};
|
||||
WorkloadView getWorkloadView(int i) {
|
||||
WorkloadView view = _views[i];
|
||||
return view;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
out vec4 varColor;
|
||||
out vec3 varTexcoord;
|
||||
|
||||
const int NUM_VERTICES_PER_SEGMENT = 2;
|
||||
const int NUM_SEGMENT_PER_VIEW_REGION = 65;
|
||||
const int NUM_VERTICES_PER_VIEW_REGION = NUM_SEGMENT_PER_VIEW_REGION * NUM_VERTICES_PER_SEGMENT;
|
||||
const int NUM_REGIONS_PER_VIEW = 3;
|
||||
const int NUM_VERTICES_PER_VIEW = NUM_VERTICES_PER_VIEW_REGION * NUM_REGIONS_PER_VIEW;
|
||||
|
||||
|
||||
layout(std140) uniform drawMeshBuffer {
|
||||
vec4 verts[NUM_SEGMENT_PER_VIEW_REGION];
|
||||
};
|
||||
|
||||
void main(void) {
|
||||
int viewID = gl_VertexID / NUM_VERTICES_PER_VIEW;
|
||||
int viewVertexID = gl_VertexID - viewID * NUM_VERTICES_PER_VIEW;
|
||||
|
||||
int regionID = viewVertexID / NUM_VERTICES_PER_VIEW_REGION;
|
||||
int regionVertexID = viewVertexID - regionID * NUM_VERTICES_PER_VIEW_REGION;
|
||||
|
||||
int segmentID = regionVertexID / NUM_VERTICES_PER_SEGMENT;
|
||||
int segmentVertexID = regionVertexID - segmentID * NUM_VERTICES_PER_SEGMENT;
|
||||
|
||||
vec4 segment = verts[segmentID];
|
||||
|
||||
vec4 spriteVert = vec4(segment.y, 0.0, segment.x, 1.0);
|
||||
vec3 spriteTan = vec3(segment.x, 0.0, -segment.y);
|
||||
|
||||
vec3 lateralDir = vec3(0.0, -1.0 + 2.0 * float(segmentVertexID), 0.0);
|
||||
|
||||
WorkloadView view = getWorkloadView(viewID);
|
||||
vec4 region = view.regions[regionID];
|
||||
vec4 proxyPosWorld = vec4(region.xyz, 1.0);
|
||||
float regionRadius = region.w;
|
||||
|
||||
// Define the sprite space
|
||||
vec3 dirZ = -normalize(view.direction_far.xyz);
|
||||
vec3 dirY = vec3(0.0, 1.0, 0.0);
|
||||
vec3 dirX = normalize(cross(dirY, dirZ));
|
||||
dirY = normalize(cross(dirZ, dirX));
|
||||
|
||||
spriteVert.xyz *= regionRadius;
|
||||
vec3 originSpaceVert = (dirX * spriteVert.x + dirY * spriteVert.y + dirZ * spriteVert.z);
|
||||
vec4 pos = vec4(proxyPosWorld.xyz + originSpaceVert, 1.0);
|
||||
|
||||
vec3 originSpaceTan = normalize(dirX * spriteTan.x + dirY * spriteTan.y + dirZ * spriteTan.z);
|
||||
|
||||
// standard transform, bring pos in view space
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
vec4 posEye;
|
||||
<$transformModelToEyePos(cam, obj, pos, posEye)$>
|
||||
vec3 tanEye;
|
||||
<$transformModelToEyeDir(cam, obj, originSpaceTan, tanEye)$>
|
||||
|
||||
lateralDir = normalize(cross(vec3(0.0, 0.0, 1.0), normalize(tanEye)));
|
||||
posEye.xyz += (0.05 * (regionID + 1)) * (-1.0 + 2.0 * float(segmentVertexID)) * lateralDir;
|
||||
|
||||
<$transformEyeToClipPos(cam, posEye, gl_Position)$>
|
||||
|
||||
varTexcoord = spriteVert.xyz;
|
||||
|
||||
// Convert region to color
|
||||
varColor = vec4(colorWheel(float(regionID) / 4.0), -1.0);
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// overlay3D.slf
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 6/16/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
<@include graphics/Light.slh@>
|
||||
<$declareLightBuffer()$>
|
||||
<$declareLightAmbientBuffer()$>
|
||||
|
||||
<@include LightingModel.slh@>
|
||||
|
||||
<@include LightDirectional.slh@>
|
||||
<$declareLightingDirectional()$>
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardCameraTransform()$>
|
||||
|
||||
vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 fresnel, float roughness, float opacity) {
|
||||
|
||||
// Need the light now
|
||||
Light light = getKeyLight();
|
||||
vec3 lightDirection = getLightDirection(light);
|
||||
vec3 lightIrradiance = getLightIrradiance(light);
|
||||
|
||||
LightAmbient ambient = getLightAmbient();
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragEyeVectorView = normalize(-position);
|
||||
vec3 fragEyeDir;
|
||||
<$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir);
|
||||
|
||||
vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient);
|
||||
|
||||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse * isDiffuseEnabled() * isDirectionalEnabled();
|
||||
color += directionalSpecular * isSpecularEnabled() * isDirectionalEnabled();
|
||||
|
||||
return vec4(color, opacity);
|
||||
}
|
||||
|
||||
uniform sampler2D originalTexture;
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec4 _positionES;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
vec4 albedo = texture(originalTexture, _texCoord0);
|
||||
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
vec3 fragNormal = normalize(_normalWS);
|
||||
vec3 fragAlbedo = albedo.rgb * _color;
|
||||
float fragMetallic = 0.0;
|
||||
vec3 fragSpecular = vec3(0.1);
|
||||
float fragRoughness = 0.9;
|
||||
float fragOpacity = albedo.a;
|
||||
|
||||
if (fragOpacity <= 0.1) {
|
||||
discard;
|
||||
}
|
||||
|
||||
vec4 color = evalGlobalColor(1.0,
|
||||
fragPosition,
|
||||
fragNormal,
|
||||
fragAlbedo,
|
||||
fragMetallic,
|
||||
fragSpecular,
|
||||
fragRoughness,
|
||||
fragOpacity);
|
||||
|
||||
|
||||
// Apply standard tone mapping
|
||||
_fragColor = vec4(pow(color.xyz, vec3(1.0 / 2.2)), color.w);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// overlay3D.slv
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Sam Gateau on 6/16/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Inputs.slh@>
|
||||
<@include gpu/Color.slh@>
|
||||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
out vec3 _color;
|
||||
out float _alpha;
|
||||
out vec2 _texCoord0;
|
||||
out vec4 _positionES;
|
||||
out vec3 _normalWS;
|
||||
|
||||
void main(void) {
|
||||
_color = color_sRGBToLinear(inColor.xyz);
|
||||
_alpha = inColor.w;
|
||||
|
||||
_texCoord0 = inTexCoord0.st;
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
|
||||
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// overlay3D.slf
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 6/16/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include DeferredGlobalLight.slh@>
|
||||
<$declareEvalSkyboxGlobalColor()$>
|
||||
|
||||
<@include graphics/Material.slh@>
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardCameraTransform()$>
|
||||
|
||||
<@include MaterialTextures.slh@>
|
||||
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$>
|
||||
|
||||
in vec2 _texCoord0;
|
||||
in vec2 _texCoord1;
|
||||
in vec4 _positionES;
|
||||
in vec3 _normalWS;
|
||||
in vec3 _color;
|
||||
in float _alpha;
|
||||
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
Material mat = getMaterial();
|
||||
BITFIELD matKey = getMaterialKey(mat);
|
||||
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$>
|
||||
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
|
||||
|
||||
float opacity = 1.0;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
albedo *= _color;
|
||||
|
||||
float metallic = getMaterialMetallic(mat);
|
||||
vec3 fresnel = getFresnelF0(metallic, albedo);
|
||||
|
||||
float roughness = getMaterialRoughness(mat);
|
||||
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
|
||||
|
||||
vec3 emissive = getMaterialEmissive(mat);
|
||||
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
|
||||
|
||||
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
|
||||
vec4 color = vec4(evalSkyboxGlobalColor(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
occlusionTex,
|
||||
fragPosition,
|
||||
normalize(_normalWS),
|
||||
albedo,
|
||||
fresnel,
|
||||
metallic,
|
||||
roughness),
|
||||
opacity);
|
||||
|
||||
// And emissive
|
||||
color.rgb += emissive * isEmissiveEnabled();
|
||||
|
||||
// Apply standard tone mapping
|
||||
_fragColor = vec4(pow(color.xyz, vec3(1.0 / 2.2)), color.w);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue