diff --git a/android/app/build.gradle b/android/app/build.gradle index 980d197397..d5058a7f40 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,5 @@ +import org.apache.tools.ant.taskdefs.condition.Os + apply plugin: 'com.android.application' android { @@ -74,10 +76,12 @@ android { // so our merge has to depend on the external native build variant.externalNativeBuildTasks.each { task -> variant.mergeResources.dependsOn(task) - def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first() - def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first() - runDumpSymsTask.dependsOn(task) - variant.assemble.dependsOn(uploadDumpSymsTask) + if (Os.isFamily(Os.FAMILY_UNIX)) { + def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first() + def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first() + runDumpSymsTask.dependsOn(task) + variant.assemble.dependsOn(uploadDumpSymsTask) + } } variant.mergeAssets.doLast { diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index f94e5f2bbf..42124bf154 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -304,6 +304,11 @@ Java_io_highfidelity_hifiinterface_MainActivity_nativeGetDisplayName(JNIEnv *env return env->NewStringUTF(username.toLatin1().data()); } +JNIEXPORT void JNICALL +Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeBeforeEnterBackground(JNIEnv *env, jobject obj) { + AndroidHelper::instance().notifyBeforeEnterBackground(); +} + JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeEnterBackground(JNIEnv *env, jobject obj) { AndroidHelper::instance().notifyEnterBackground(); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index aafde836c1..8fd8b9d0e6 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -59,6 +59,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager); private native void nativeOnDestroy(); private native void nativeGotoUrl(String url); + private native void nativeBeforeEnterBackground(); private native void nativeEnterBackground(); private native void nativeEnterForeground(); private native long nativeOnExitVr(); @@ -291,6 +292,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW case "Home": case "Privacy Policy": case "Login": { + nativeBeforeEnterBackground(); Intent intent = new Intent(this, MainActivity.class); intent.putExtra(MainActivity.EXTRA_FRAGMENT, activityName); intent.putExtra(MainActivity.EXTRA_BACK_TO_SCENE, backToScene); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java index b98849d051..9ca6c7c4cc 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java @@ -4,6 +4,7 @@ import android.app.Fragment; import android.content.Context; import android.os.Bundle; import android.os.Handler; +import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Editable; @@ -32,6 +33,7 @@ public class HomeFragment extends Fragment { private OnHomeInteractionListener mListener; + private SwipeRefreshLayout mSwipeRefreshLayout; public native String nativeGetLastLocation(); @@ -57,6 +59,7 @@ public class HomeFragment extends Fragment { View rootView = inflater.inflate(R.layout.fragment_home, container, false); searchNoResultsView = rootView.findViewById(R.id.searchNoResultsView); + mSwipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout); mDomainsView = rootView.findViewById(R.id.rvDomains); int numberOfColumns = 1; @@ -76,12 +79,14 @@ public class HomeFragment extends Fragment { searchNoResultsView.setText(R.string.search_no_results); searchNoResultsView.setVisibility(View.VISIBLE); mDomainsView.setVisibility(View.GONE); + mSwipeRefreshLayout.setRefreshing(false); } @Override public void onNonEmptyAdapter() { searchNoResultsView.setVisibility(View.GONE); mDomainsView.setVisibility(View.VISIBLE); + mSwipeRefreshLayout.setRefreshing(false); } @Override @@ -104,7 +109,7 @@ public class HomeFragment extends Fragment { @Override public void afterTextChanged(Editable editable) { - mDomainAdapter.loadDomains(editable.toString()); + mDomainAdapter.loadDomains(editable.toString(), false); if (editable.length() > 0) { mSearchIconView.setVisibility(View.GONE); mClearSearch.setVisibility(View.VISIBLE); @@ -130,6 +135,13 @@ public class HomeFragment extends Fragment { mClearSearch.setOnClickListener(view -> onSearchClear(view)); + mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + mDomainAdapter.loadDomains(mSearchView.getText().toString(), true); + } + }); + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); return rootView; diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/DomainProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/DomainProvider.java index 7a2101a229..602fadc37e 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/DomainProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/DomainProvider.java @@ -10,7 +10,7 @@ import io.highfidelity.hifiinterface.view.DomainAdapter; public interface DomainProvider { - void retrieve(String filterText, DomainCallback domainCallback); + void retrieve(String filterText, DomainCallback domainCallback, boolean forceRefresh); interface DomainCallback { void retrieveOk(List domain); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UserStoryDomainProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UserStoryDomainProvider.java index ca5e0c17bd..e3b631bd69 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UserStoryDomainProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UserStoryDomainProvider.java @@ -49,8 +49,8 @@ public class UserStoryDomainProvider implements DomainProvider { } @Override - public synchronized void retrieve(String filterText, DomainCallback domainCallback) { - if (!startedToGetFromAPI) { + public synchronized void retrieve(String filterText, DomainCallback domainCallback, boolean forceRefresh) { + if (!startedToGetFromAPI || forceRefresh) { startedToGetFromAPI = true; fillDestinations(filterText, domainCallback); } else { @@ -72,6 +72,7 @@ public class UserStoryDomainProvider implements DomainProvider { allStories.clear(); getUserStoryPage(1, allStories, null, ex -> { + suggestions.clear(); allStories.forEach(userStory -> { if (taggedStoriesIds.contains(userStory.id)) { userStory.tagFound = true; diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java index 4f8b33b481..71d634e9ea 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -42,14 +42,14 @@ public class DomainAdapter extends RecyclerView.Adapter domain) { @@ -76,7 +76,7 @@ public class DomainAdapter extends RecyclerView.Adapter domain) { diff --git a/android/app/src/main/res/layout/fragment_home.xml b/android/app/src/main/res/layout/fragment_home.xml index cb39b8f69e..0f8f437c04 100644 --- a/android/app/src/main/res/layout/fragment_home.xml +++ b/android/app/src/main/res/layout/fragment_home.xml @@ -63,13 +63,18 @@ android:visibility="gone" /> - - + android:layout_height="0dp"> + + diff --git a/android/build.gradle b/android/build.gradle index 39265327b2..bc39c30472 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -71,17 +71,17 @@ def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a') def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms") -def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz' -def qtChecksum='04599670ccca84bd2b15f6915568eb2d' -def qtVersionId='8QbCma4ryEPgBYn_8kgYgB10IvNx9I1W' +def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl.tgz' +def qtChecksum='f312c47cd8b8dbca824c32af4eec5e66' +def qtVersionId='nyCGcb91S4QbYeJhUkawO5x1lrLdSNB_' if (Os.isFamily(Os.FAMILY_MAC)) { - qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz' - qtChecksum='4b02de9d67d6bfb202355a808d2d9c59' - qtVersionId='2gfgoYCggJGyXxKiazaPGsMs1Gn9j4og' + qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl.tgz' + qtChecksum='a0c8b394aec5b0fcd46714ca3a53278a' + qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { - qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz' - qtChecksum='c3e25db64002d0f43cf565e0ef708911' - qtVersionId='xKIteC6HO0xrmcWeMmhQcmKyPEsnUrcZ' + qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl.tgz' + qtChecksum='d80aed4233ce9e222aae8376e7a94bf9' + qtVersionId='iDVXu0i3WEXRFIxQCtzcJ2XuKrE8RIqB' } def packages = [ diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index e0c35b7148..e79473783a 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1042,7 +1043,7 @@ bool AssetServer::loadMappingsFromFile() { bool AssetServer::writeMappingsToFile() { auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME); - QFile mapFile { mapFilePath }; + QSaveFile mapFile { mapFilePath }; if (mapFile.open(QIODevice::WriteOnly)) { QJsonObject root; @@ -1053,8 +1054,12 @@ bool AssetServer::writeMappingsToFile() { QJsonDocument jsonDocument { root }; if (mapFile.write(jsonDocument.toJson()) != -1) { - qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath; - return true; + if (mapFile.commit()) { + qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath; + return true; + } else { + qCWarning(asset_server) << "Failed to commit JSON mappings to file at" << mapFilePath; + } } else { qCWarning(asset_server) << "Failed to write JSON mappings to file at" << mapFilePath; } diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index 968a65a6dd..de9f8a22fa 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -66,6 +66,13 @@ elseif ((NOT MSVC12) AND (NOT MSVC14)) endif() endif () +if (CMAKE_GENERATOR STREQUAL "Xcode") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") + set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES") + set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym") + set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES") +endif() + if (APPLE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") diff --git a/cmake/externals/GifCreator/CMakeLists.txt b/cmake/externals/GifCreator/CMakeLists.txt index 127bdf28f5..094f5612a9 100644 --- a/cmake/externals/GifCreator/CMakeLists.txt +++ b/cmake/externals/GifCreator/CMakeLists.txt @@ -3,7 +3,7 @@ set(EXTERNAL_NAME GifCreator) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/GifCreator.zip + URL https://public.highfidelity.com/dependencies/GifCreator.zip URL_MD5 8ac8ef5196f47c658dce784df5ecdb70 CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index ed76f181e7..ae4cf6320e 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -17,7 +17,7 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.26.0_public.zip + URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip URL_MD5 06804ff9727b910dcd04a37c800053b5 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" /CMakeLists.txt @@ -38,7 +38,7 @@ elseif(APPLE) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://static.oculus.com/sdk-downloads/ovr_sdk_macos_0.5.0.1.tar.gz + URL https://public.highfidelity.com/dependencies/ovr_sdk_macos_0.5.0.1.tar.gz URL_MD5 0a0785a04fb285f64f62267388344ad6 CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/LibOVRPlatform/CMakeLists.txt b/cmake/externals/LibOVRPlatform/CMakeLists.txt index 3622972a13..895efa9357 100644 --- a/cmake/externals/LibOVRPlatform/CMakeLists.txt +++ b/cmake/externals/LibOVRPlatform/CMakeLists.txt @@ -9,7 +9,7 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/OVRPlatformSDK_v1.10.0.zip + URL https://public.highfidelity.com/dependencies/OVRPlatformSDK_v1.10.0.zip URL_MD5 e6c8264af16d904e6506acd5172fa0a9 CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/boostconfig/CMakeLists.txt b/cmake/externals/boostconfig/CMakeLists.txt index 0adb349589..e33167b0ba 100644 --- a/cmake/externals/boostconfig/CMakeLists.txt +++ b/cmake/externals/boostconfig/CMakeLists.txt @@ -5,7 +5,7 @@ include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} #URL https://github.com/boostorg/config/archive/boost-1.58.0.zip - URL http://hifi-public.s3.amazonaws.com/dependencies/config-boost-1.58.0.zip + URL https://public.highfidelity.com/dependencies/config-boost-1.58.0.zip URL_MD5 42fa673bae2b7645a22736445e80eb8d CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index 91860a7470..ffa1c67ce3 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -17,7 +17,7 @@ include(ExternalProject) if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz + URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz URL_MD5 0a6876607ebe83e227427215f15946fd CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0 LOG_DOWNLOAD 1 @@ -28,7 +28,7 @@ if (WIN32) else () ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz + URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz URL_MD5 0a6876607ebe83e227427215f15946fd CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 diff --git a/cmake/externals/crashpad/CMakeLists.txt b/cmake/externals/crashpad/CMakeLists.txt index e509e115e4..34348b6418 100644 --- a/cmake/externals/crashpad/CMakeLists.txt +++ b/cmake/externals/crashpad/CMakeLists.txt @@ -6,7 +6,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://public.highfidelity.com/dependencies/crashpad_062317.1.zip + URL https://public.highfidelity.com/dependencies/crashpad_062317.1.zip URL_MD5 9c84b77f5f23daf939da1371825ed2b1 CONFIGURE_COMMAND "" BUILD_COMMAND "" @@ -16,19 +16,46 @@ if (WIN32) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of Crashpad include directories") - + set(BIN_RELEASE_PATH "${SOURCE_DIR}/out/Release_x64") + set(BIN_EXT ".exe") + set(LIB_RELEASE_PATH "${SOURCE_DIR}/out/Release_x64/lib_MD") + set(LIB_DEBUG_PATH "${SOURCE_DIR}/out/Debug_x64/lib_MD") + set(LIB_PREFIX "") set(LIB_EXT "lib") +elseif (APPLE) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://public.highfidelity.com/dependencies/crashpad_mac_070318.zip + URL_MD5 ba1501dc163591ac2d1be74946967e2a + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/out/Release_x64/lib_MD/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad release library") - set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_RELEASE ${SOURCE_DIR}/out/Release_x64/lib_MD/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base release library") - set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_RELEASE ${SOURCE_DIR}/out/Release_x64/lib_MD/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util release library") + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${SOURCE_DIR}/out/Debug_x64/lib_MD/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad debug library") - set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_DEBUG ${SOURCE_DIR}/out/Debug_x64/lib_MD/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base debug library") - set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_DEBUG ${SOURCE_DIR}/out/Debug_x64/lib_MD/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util debug library") + set(BIN_RELEASE_PATH "${SOURCE_DIR}/out/Release") + set(BIN_EXT "") + set(LIB_RELEASE_PATH "${SOURCE_DIR}/out/Release/lib") + set(LIB_DEBUG_PATH "${SOURCE_DIR}/out/Debug/lib") + set(LIB_PREFIX "lib") + set(LIB_EXT "a") +endif () - set(CRASHPAD_HANDLER_EXE_PATH ${SOURCE_DIR}/out/Release_x64/crashpad_handler.exe CACHE FILEPATH "Path to the Crashpad handler executable") +if (WIN32 OR APPLE) + + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of Crashpad include directories") + + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${LIB_RELEASE_PATH}/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad release library") + set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_RELEASE ${LIB_RELEASE_PATH}/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base release library") + set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_RELEASE ${LIB_RELEASE_PATH}/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util release library") + + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${LIB_DEBUG_PATH}/${LIB_PREFIX}crashpad_client.${LIB_EXT} CACHE FILEPATH "Path to Crashpad debug library") + set(${EXTERNAL_NAME_UPPER}_BASE_LIBRARY_DEBUG ${LIB_DEBUG_PATH}/${LIB_PREFIX}base.${LIB_EXT} CACHE FILEPATH "Path to Crashpad base debug library") + set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY_DEBUG ${LIB_DEBUG_PATH}/${LIB_PREFIX}crashpad_util.${LIB_EXT} CACHE FILEPATH "Path to Crashpad util debug library") + + set(CRASHPAD_HANDLER_EXE_PATH ${BIN_RELEASE_PATH}/crashpad_handler${BIN_EXT} CACHE FILEPATH "Path to the Crashpad handler binary") endif () # Hide this external target (for ide users) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 6a894e76b6..28a2177cbb 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -11,7 +11,7 @@ endif () include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.1.0.zip + URL https://public.highfidelity.com/dependencies/draco-1.1.0.zip URL_MD5 208f8b04c91d5f1c73d731a3ea37c5bb CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=-$ ${EXTRA_CMAKE_FLAGS} LOG_DOWNLOAD 1 diff --git a/cmake/externals/etc2comp/CMakeLists.txt b/cmake/externals/etc2comp/CMakeLists.txt index b497212625..88ed988873 100644 --- a/cmake/externals/etc2comp/CMakeLists.txt +++ b/cmake/externals/etc2comp/CMakeLists.txt @@ -15,7 +15,7 @@ include(ExternalProject) # that would override CMAKE_CXX_FLAGS ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip + URL https://public.highfidelity.com/dependencies/etc2comp-patched.zip URL_MD5 4c96153eb179acbe619e3d99d3330595 CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS} BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build diff --git a/cmake/externals/glad32es/CMakeLists.txt b/cmake/externals/glad32es/CMakeLists.txt index 8ca4d44021..04000b4cfe 100644 --- a/cmake/externals/glad32es/CMakeLists.txt +++ b/cmake/externals/glad32es/CMakeLists.txt @@ -5,7 +5,7 @@ include(SelectLibraryConfigurations) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/austin/glad/glad32es.zip + URL https://public.highfidelity.com/dependencies/glad/glad32es.zip URL_MD5 6a641d8c49dee4c895fa59315f5682a6 CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON LOG_DOWNLOAD 1 diff --git a/cmake/externals/glad41/CMakeLists.txt b/cmake/externals/glad41/CMakeLists.txt index 2371044362..0c99a03025 100644 --- a/cmake/externals/glad41/CMakeLists.txt +++ b/cmake/externals/glad41/CMakeLists.txt @@ -5,7 +5,7 @@ include(SelectLibraryConfigurations) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/austin/glad/glad41.zip + URL https://public.highfidelity.com/dependencies/glad/glad41.zip URL_MD5 1324eeec33abe91e67d19ae551ba624d CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON LOG_DOWNLOAD 1 diff --git a/cmake/externals/glad45/CMakeLists.txt b/cmake/externals/glad45/CMakeLists.txt index 8ad455fa7e..112f6f3592 100644 --- a/cmake/externals/glad45/CMakeLists.txt +++ b/cmake/externals/glad45/CMakeLists.txt @@ -5,7 +5,7 @@ include(SelectLibraryConfigurations) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/austin/glad/glad45.zip + URL https://public.highfidelity.com/dependencies/glad/glad45.zip URL_MD5 cfb19b3cb5b2f8f1d1669fb3150e5f05 CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON LOG_DOWNLOAD 1 diff --git a/cmake/externals/gli/CMakeLists.txt b/cmake/externals/gli/CMakeLists.txt index 2ef4d2b3af..bde31cbede 100644 --- a/cmake/externals/gli/CMakeLists.txt +++ b/cmake/externals/gli/CMakeLists.txt @@ -3,7 +3,7 @@ set(EXTERNAL_NAME gli) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/gli-0.8.1.0.zip + URL https://public.highfidelity.com/dependencies/gli-0.8.1.0.zip URL_MD5 00c990f59c12bbf367956ef399d6f798 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CONFIGURE_COMMAND "" diff --git a/cmake/externals/glm/CMakeLists.txt b/cmake/externals/glm/CMakeLists.txt index 0a83004438..a52ddde9f5 100644 --- a/cmake/externals/glm/CMakeLists.txt +++ b/cmake/externals/glm/CMakeLists.txt @@ -3,7 +3,7 @@ set(EXTERNAL_NAME glm) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.5-patched.zip + URL https://public.highfidelity.com/dependencies/glm-0.9.8.5-patched.zip URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= ${EXTERNAL_ARGS} diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt index e3ba36a440..8a8e2573d5 100644 --- a/cmake/externals/hifiAudioCodec/CMakeLists.txt +++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt @@ -6,16 +6,16 @@ set(EXTERNAL_NAME hifiAudioCodec) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) if (WIN32) - set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip) + set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-win-2.0.zip) set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67) elseif (APPLE) - set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip) + set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-mac-2.0.zip) set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba) elseif (ANDROID) - set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip) + set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-android-2.0.zip) set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683) elseif (UNIX) - set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip) + set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/codecSDK-linux-2.0.zip) set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481) else() return() diff --git a/cmake/externals/neuron/CMakeLists.txt b/cmake/externals/neuron/CMakeLists.txt index 76dda8f8c5..5ac38bc442 100644 --- a/cmake/externals/neuron/CMakeLists.txt +++ b/cmake/externals/neuron/CMakeLists.txt @@ -4,7 +4,7 @@ set(EXTERNAL_NAME neuron) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.2.zip") +set(NEURON_URL "https://public.highfidelity.com/dependencies/neuron_datareader_b.12.2.zip") set(NEURON_URL_MD5 "84273ad2200bf86a9279d1f412a822ca") ExternalProject_Add(${EXTERNAL_NAME} diff --git a/cmake/externals/nvtt/CMakeLists.txt b/cmake/externals/nvtt/CMakeLists.txt index 3076217c33..2db8335cd7 100644 --- a/cmake/externals/nvtt/CMakeLists.txt +++ b/cmake/externals/nvtt/CMakeLists.txt @@ -8,7 +8,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.hifi.zip + URL https://public.highfidelity.com/dependencies/nvtt-win-2.1.0.hifi.zip URL_MD5 10da01cf601f88f6dc12a6bc13c89136 CONFIGURE_COMMAND "" BUILD_COMMAND "" @@ -29,7 +29,7 @@ else () ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.hifi-83462e4.zip + URL https://public.highfidelity.com/dependencies/nvidia-texture-tools-2.1.0.hifi-83462e4.zip URL_MD5 602776e08515b54bfa1b8dc455003f0f CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON LOG_DOWNLOAD 1 diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index cb4aafcf8b..05dfe70ed7 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -7,7 +7,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://github.com/ValveSoftware/openvr/archive/v1.0.6.zip + URL https://public.highfidelity.com/dependencies/openvr-1.0.6.zip URL_MD5 f6892cd3a3078f505d03b4297f5a1951 CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt index c799b45e78..a92c07da86 100644 --- a/cmake/externals/polyvox/CMakeLists.txt +++ b/cmake/externals/polyvox/CMakeLists.txt @@ -3,7 +3,7 @@ set(EXTERNAL_NAME polyvox) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox-master-2015-7-15.zip + URL https://public.highfidelity.com/dependencies/polyvox-master-2015-7-15.zip URL_MD5 9ec6323b87e849ae36e562ae1c7494a9 CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF -DCMAKE_INSTALL_PREFIX:PATH= BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 7bf6f05d9f..6960f7682a 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -13,7 +13,7 @@ endif () ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/quazip-0.7.3.zip + URL https://public.highfidelity.com/dependencies/quazip-0.7.3.zip URL_MD5 ed03754d39b9da1775771819b8001d45 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index cb61516b9a..1e8e690743 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -7,7 +7,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-devel-2.0.3-VC.zip + URL https://public.highfidelity.com/dependencies/SDL2-devel-2.0.3-VC.zip URL_MD5 30a333bcbe94bc5016e8799c73e86233 CONFIGURE_COMMAND "" BUILD_COMMAND "" @@ -18,7 +18,8 @@ elseif (APPLE) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip + URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.zip + URL_MD5 55f1eae5142d20db11c844d8d4d6deed CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DVIDEO_OPENGL=OFF BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build LOG_DOWNLOAD 1 @@ -49,7 +50,7 @@ else () ExternalProject_Add( ${EXTERNAL_NAME} - URL http://www.libsdl.org/release/SDL2-2.0.3.tar.gz + URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.tar.gz URL_MD5 fe6c61d2e9df9ef570e7e80c6e822537 CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= LOG_DOWNLOAD 1 diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index bd0d042c0b..17d2f98e2d 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -4,15 +4,15 @@ set(EXTERNAL_NAME sixense) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip") +#set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_062612.zip") #set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc") #set(SIXENSE_NEW_LAYOUT 0) -set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip") +set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_071615.zip") set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d") set(SIXENSE_NEW_LAYOUT 1) -#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip") +#set(SIXENSE_URL "https://public.highfidelity.com/dependencies/SixenseSDK_102215.zip") #set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1") #set(SIXENSE_NEW_LAYOUT 1) diff --git a/cmake/externals/steamworks/CMakeLists.txt b/cmake/externals/steamworks/CMakeLists.txt index 152e95cdcf..30b3926436 100644 --- a/cmake/externals/steamworks/CMakeLists.txt +++ b/cmake/externals/steamworks/CMakeLists.txt @@ -4,7 +4,7 @@ set(EXTERNAL_NAME steamworks) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(STEAMWORKS_URL "https://s3.amazonaws.com/hifi-public/dependencies/steamworks_sdk_137.zip") +set(STEAMWORKS_URL "https://public.highfidelity.com/dependencies/steamworks_sdk_137.zip") set(STEAMWORKS_URL_MD5 "95ba9d0e3ddc04f8a8be17d2da806cbb") ExternalProject_Add( diff --git a/cmake/externals/tbb/CMakeLists.txt b/cmake/externals/tbb/CMakeLists.txt index 9664fe7250..436cae79a1 100644 --- a/cmake/externals/tbb/CMakeLists.txt +++ b/cmake/externals/tbb/CMakeLists.txt @@ -3,13 +3,13 @@ set(EXTERNAL_NAME tbb) include(ExternalProject) if (WIN32) - set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_win_slim.zip) + set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_win_slim.zip) set(DOWNLOAD_MD5 065934458e3db88397f3d10e7eea536c) elseif (APPLE) - set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz) + set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_mac_slim.tar.gz) set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105) else () - set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz) + set(DOWNLOAD_URL https://public.highfidelity.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz) set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c) endif () diff --git a/cmake/externals/vhacd/CMakeLists.txt b/cmake/externals/vhacd/CMakeLists.txt index 11afa255f1..fe19f7ac7a 100644 --- a/cmake/externals/vhacd/CMakeLists.txt +++ b/cmake/externals/vhacd/CMakeLists.txt @@ -7,7 +7,7 @@ endif () include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/v-hacd-master.zip + URL https://public.highfidelity.com/dependencies/v-hacd-master.zip URL_MD5 3bfc94f8dd3dfbfe8f4dc088f4820b3e CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 4c0ffaf88f..8b3408e3fa 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,7 +6,7 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi10.zip + URL https://public.highfidelity.com/dependencies/qtaudio_wasapi10.zip URL_MD5 4f40e49715a420fb67b45b9cee19052c CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/zlib/CMakeLists.txt b/cmake/externals/zlib/CMakeLists.txt index 3bbda322a1..85506ba0e1 100644 --- a/cmake/externals/zlib/CMakeLists.txt +++ b/cmake/externals/zlib/CMakeLists.txt @@ -5,7 +5,8 @@ include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/zlib128.zip + URL https://public.highfidelity.com/dependencies/zlib128.zip + URL_MD5 126f8676442ffbd97884eb4d6f32afb4 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build LOG_DOWNLOAD 1 diff --git a/cmake/macros/AddCrashpad.cmake b/cmake/macros/AddCrashpad.cmake index 7d161be7f0..113ab53aae 100644 --- a/cmake/macros/AddCrashpad.cmake +++ b/cmake/macros/AddCrashpad.cmake @@ -23,7 +23,7 @@ macro(add_crashpad) set(CMAKE_BACKTRACE_TOKEN $ENV{CMAKE_BACKTRACE_TOKEN}) endif() - if (WIN32 AND USE_CRASHPAD) + if ((WIN32 OR APPLE) AND USE_CRASHPAD) get_property(CRASHPAD_CHECKED GLOBAL PROPERTY CHECKED_FOR_CRASHPAD_ONCE) if (NOT CRASHPAD_CHECKED) @@ -42,6 +42,10 @@ macro(add_crashpad) if (WIN32) set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "/ignore:4099") + elseif (APPLE) + find_library(Security Security) + target_link_libraries(${TARGET_NAME} ${Security}) + target_link_libraries(${TARGET_NAME} "-lbsm") endif() add_custom_command( diff --git a/interface/resources/html/img/tablet-help-gamepad.jpg b/interface/resources/html/img/tablet-help-gamepad.jpg index 2594cbd86c..bc6dcacee0 100644 Binary files a/interface/resources/html/img/tablet-help-gamepad.jpg and b/interface/resources/html/img/tablet-help-gamepad.jpg differ diff --git a/interface/resources/html/img/tablet-help-keyboard.jpg b/interface/resources/html/img/tablet-help-keyboard.jpg index 1c257f83e2..7045abed75 100644 Binary files a/interface/resources/html/img/tablet-help-keyboard.jpg and b/interface/resources/html/img/tablet-help-keyboard.jpg differ diff --git a/interface/resources/html/img/tablet-help-oculus.jpg b/interface/resources/html/img/tablet-help-oculus.jpg index bbff760e96..7e2062400a 100644 Binary files a/interface/resources/html/img/tablet-help-oculus.jpg and b/interface/resources/html/img/tablet-help-oculus.jpg differ diff --git a/interface/resources/html/img/tablet-help-vive.jpg b/interface/resources/html/img/tablet-help-vive.jpg index 849e3a5588..27b97d71bd 100644 Binary files a/interface/resources/html/img/tablet-help-vive.jpg and b/interface/resources/html/img/tablet-help-vive.jpg differ diff --git a/interface/resources/html/img/tablet-help-windowsMR.jpg b/interface/resources/html/img/tablet-help-windowsMR.jpg new file mode 100644 index 0000000000..b9d0241bec Binary files /dev/null and b/interface/resources/html/img/tablet-help-windowsMR.jpg differ diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index a6588be083..279213bbcb 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -66,7 +66,7 @@ diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2406fa048d..bff13cea54 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -263,6 +263,12 @@ Item { } StatText { text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms" + } + StatText { + text: "GPU (Per pixel): " + root.gpuFrameTimePerPixel.toFixed(5) + " ns/pp" + } + StatText { + text: "GPU frame size: " + root.gpuFrameSize.x + " x " + root.gpuFrameSize.y } StatText { text: "Triangles: " + root.triangles + diff --git a/interface/resources/qml/controls-uit/+android/Button.qml b/interface/resources/qml/controls-uit/+android/Button.qml deleted file mode 100644 index 2f05b35685..0000000000 --- a/interface/resources/qml/controls-uit/+android/Button.qml +++ /dev/null @@ -1,125 +0,0 @@ -// -// Button.qml -// -// Created by David Rowe on 16 Feb 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 -import TabletScriptingInterface 1.0 - -import "../styles-uit" - -Original.Button { - id: root; - - property int color: 0 - property int colorScheme: hifi.colorSchemes.light - property string buttonGlyph: ""; - - width: hifi.dimensions.buttonWidth - height: hifi.dimensions.controlLineHeight - - HifiConstants { id: hifi } - - onHoveredChanged: { - if (hovered) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - onFocusChanged: { - if (focus) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - onClicked: { - Tablet.playSound(TabletEnums.ButtonClick); - } - - style: ButtonStyle { - - background: Rectangle { - radius: hifi.buttons.radius - - border.width: (control.color === hifi.buttons.none || - (control.color === hifi.buttons.noneBorderless && control.hovered) || - (control.color === hifi.buttons.noneBorderlessWhite && control.hovered) || - (control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0; - border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight : - (control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white); - - gradient: Gradient { - GradientStop { - position: 0.2 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorStart[control.colorScheme] - } else if (control.pressed) { - 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] - } - } - } - GradientStop { - position: 1.0 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorFinish[control.colorScheme] - } else if (control.pressed) { - 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] - } - } - } - } - } - - label: Item { - HiFiGlyphs { - id: buttonGlyph; - visible: root.buttonGlyph !== ""; - text: root.buttonGlyph === "" ? hifi.glyphs.question : root.buttonGlyph; - // Size - size: 34; - // Anchors - anchors.right: buttonText.left; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - // Style - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme]; - // Alignment - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - } - RalewayBold { - id: buttonText; - anchors.centerIn: parent; - font.capitalization: Font.AllUppercase - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme] - size: hifi.fontSizes.buttonLabel - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.text - } - } - } -} diff --git a/interface/resources/qml/controls-uit/+android/Table.qml b/interface/resources/qml/controls-uit/+android/Table.qml deleted file mode 100644 index 3c1d0fcd3c..0000000000 --- a/interface/resources/qml/controls-uit/+android/Table.qml +++ /dev/null @@ -1,165 +0,0 @@ -// -// Table.qml -// -// Created by David Rowe on 18 Feb 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Controls 2.2 as QQC2 - -import "../styles-uit" - -TableView { - id: tableView - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - property bool expandSelectedRow: false - property bool centerHeaderText: false - readonly property real headerSpacing: 3 //spacing between sort indicator and table header title - property var titlePaintedPos: [] // storing extra data position behind painted - // title text and sort indicatorin table's header - signal titlePaintedPosSignal(int column) //signal that extradata position gets changed - - model: ListModel { } - - Component.onCompleted: { - if (flickableItem !== null && flickableItem !== undefined) { - tableView.flickableItem.QQC2.ScrollBar.vertical = scrollbar - } - } - - QQC2.ScrollBar { - id: scrollbar - parent: tableView.flickableItem - policy: QQC2.ScrollBar.AsNeeded - orientation: Qt.Vertical - visible: size < 1.0 - topPadding: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1 - anchors.top: tableView.top - anchors.left: tableView.right - anchors.bottom: tableView.bottom - - background: Item { - implicitWidth: hifi.dimensions.scrollbarBackgroundWidth - Rectangle { - anchors { - fill: parent; - topMargin: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight : 0 - } - color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight - : hifi.colors.tableScrollBackgroundDark - } - } - - contentItem: Item { - implicitWidth: hifi.dimensions.scrollbarHandleWidth - Rectangle { - anchors.fill: parent - radius: (width - 4)/2 - color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark - } - } - } - - headerVisible: false - headerDelegate: Rectangle { - height: hifi.dimensions.tableHeaderHeight - color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - - - RalewayRegular { - id: titleText - x: centerHeaderText ? (parent.width - paintedWidth - - ((sortIndicatorVisible && - sortIndicatorColumn === styleData.column) ? - (titleSort.paintedWidth / 5 + tableView.headerSpacing) : 0)) / 2 : - hifi.dimensions.tablePadding - text: styleData.value - size: hifi.fontSizes.tableHeading - font.capitalization: Font.AllUppercase - color: hifi.colors.baseGrayHighlight - horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft) - anchors.verticalCenter: parent.verticalCenter - } - - //actual image of sort indicator in glyph font only 20% of real font size - //i.e. if the charachter size set to 60 pixels, actual image is 12 pixels - HiFiGlyphs { - id: titleSort - text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn - color: hifi.colors.darkGray - opacity: 0.6; - size: hifi.fontSizes.tableHeadingIcon - anchors.verticalCenter: titleText.verticalCenter - anchors.left: titleText.right - anchors.leftMargin: -(hifi.fontSizes.tableHeadingIcon / 2.5) + tableView.headerSpacing - visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column - onXChanged: { - titlePaintedPos[styleData.column] = titleText.x + titleText.paintedWidth + - paintedWidth / 5 + tableView.headerSpacing*2 - titlePaintedPosSignal(styleData.column) - } - } - - Rectangle { - width: 1 - anchors { - left: parent.left - top: parent.top - topMargin: 1 - bottom: parent.bottom - bottomMargin: 2 - } - color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - visible: styleData.column > 0 - } - - Rectangle { - height: 1 - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - } - } - - // Use rectangle to draw border with rounded corners. - frameVisible: false - Rectangle { - color: "#00000000" - anchors { fill: parent; margins: -2 } - border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - border.width: 2 - } - anchors.margins: 2 // Shrink TableView to lie within border. - - backgroundVisible: true - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - style: TableViewStyle { - // Needed in order for rows to keep displaying rows after end of table entries. - backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd - padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0 - } - - rowDelegate: Rectangle { - height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight - color: styleData.selected - ? hifi.colors.primaryHighlight - : tableView.isLightColorScheme - ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) - : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) - } -} diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index 9ec5ed19ba..be8c9f6740 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -46,7 +46,6 @@ FocusScope { hoverEnabled: true visible: true height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. - textRole: "text" function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; } function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; } diff --git a/interface/resources/qml/desktop/+android/Desktop.qml b/interface/resources/qml/desktop/+android/Desktop.qml deleted file mode 100644 index 6a68f63d0a..0000000000 --- a/interface/resources/qml/desktop/+android/Desktop.qml +++ /dev/null @@ -1,575 +0,0 @@ -// -// Desktop.qml -// -// Created by Bradley Austin Davis on 15 Apr 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 -// - -import QtQuick 2.7 -import QtQuick.Controls 1.4 - -import "../dialogs" -import "../js/Utils.js" as Utils - -// This is our primary 'desktop' object to which all VR dialogs and windows are childed. -FocusScope { - id: desktop - objectName: "desktop" - anchors.fill: parent - - readonly property int invalid_position: -9999; - property rect recommendedRect: Qt.rect(0,0,0,0); - property var expectedChildren; - property bool repositionLocked: true - property bool hmdHandMouseActive: false - - onRepositionLockedChanged: { - if (!repositionLocked) { - d.handleSizeChanged(); - } - - } - - onHeightChanged: d.handleSizeChanged(); - - onWidthChanged: d.handleSizeChanged(); - - // Controls and windows can trigger this signal to ensure the desktop becomes visible - // when they're opened. - signal showDesktop(); - - // This is for JS/QML communication, which is unused in the Desktop, - // but not having this here results in spurious warnings about a - // missing signal - signal sendToScript(var message); - - // Allows QML/JS to find the desktop through the parent chain - property bool desktopRoot: true - - // The VR version of the primary menu - property var rootMenu: Menu { - id: rootMenuId - objectName: "rootMenu" - - property var exclusionGroups: ({}); - property Component exclusiveGroupMaker: Component { - ExclusiveGroup { - } - } - - function addExclusionGroup(qmlAction, exclusionGroup) { - - var exclusionGroupId = exclusionGroup.toString(); - if(!exclusionGroups[exclusionGroupId]) { - exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(rootMenuId); - } - - qmlAction.exclusiveGroup = exclusionGroups[exclusionGroupId] - } - } - - // FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX/AMD - // because shaders are 4.2, and do not include #version declarations. - property bool gradientsSupported: Qt.platform.os != "osx" && !~GL.vendor.indexOf("ATI") - - readonly property alias zLevels: zLevels - QtObject { - id: zLevels; - readonly property real normal: 1 // make windows always appear higher than QML overlays and other non-window controls. - readonly property real top: 2000 - readonly property real modal: 4000 - readonly property real menu: 8000 - } - - QtObject { - id: d - - function handleSizeChanged() { - if (desktop.repositionLocked) { - return; - } - var oldRecommendedRect = recommendedRect; - var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect(); - var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y, - newRecommendedRectJS.width, - newRecommendedRectJS.height); - - var oldChildren = expectedChildren; - var newChildren = d.getRepositionChildren(); - if (oldRecommendedRect != Qt.rect(0,0,0,0) && oldRecommendedRect != Qt.rect(0,0,1,1) - && (oldRecommendedRect != newRecommendedRect - || oldChildren != newChildren) - ) { - expectedChildren = newChildren; - d.repositionAll(); - } - recommendedRect = newRecommendedRect; - } - - function findChild(item, name) { - for (var i = 0; i < item.children.length; ++i) { - if (item.children[i].objectName === name) { - return item.children[i]; - } - } - return null; - } - - function findParentMatching(item, predicate) { - while (item) { - if (predicate(item)) { - break; - } - item = item.parent; - } - return item; - } - - function findMatchingChildren(item, predicate) { - var results = []; - for (var i in item.children) { - var child = item.children[i]; - if (predicate(child)) { - results.push(child); - } - } - return results; - } - - function isTopLevelWindow(item) { - return item.topLevelWindow; - } - - function isAlwaysOnTopWindow(window) { - return window.alwaysOnTop; - } - - function isModalWindow(window) { - return window.modality !== Qt.NonModal; - } - - function getTopLevelWindows(predicate) { - return findMatchingChildren(desktop, function(child) { - return (isTopLevelWindow(child) && (!predicate || predicate(child))); - }); - } - - function getDesktopWindow(item) { - return findParentMatching(item, isTopLevelWindow) - } - - function fixupZOrder(windows, basis, topWindow) { - windows.sort(function(a, b){ return a.z - b.z; }); - - if ((topWindow.z >= basis) && (windows[windows.length - 1] === topWindow)) { - return; - } - - var lastZ = -1; - var lastTargetZ = basis - 1; - for (var i = 0; i < windows.length; ++i) { - var window = windows[i]; - if (!window.visible) { - continue - } - - if (topWindow && (topWindow === window)) { - continue - } - - if (window.z > lastZ) { - lastZ = window.z; - ++lastTargetZ; - } - if (DebugQML) { - console.log("Assigning z order " + lastTargetZ + " to " + window) - } - - window.z = lastTargetZ; - } - if (topWindow) { - ++lastTargetZ; - if (DebugQML) { - console.log("Assigning z order " + lastTargetZ + " to " + topWindow) - } - topWindow.z = lastTargetZ; - } - - return lastTargetZ; - } - - function raiseWindow(targetWindow) { - var predicate; - var zBasis; - if (isModalWindow(targetWindow)) { - predicate = isModalWindow; - zBasis = zLevels.modal - } else if (isAlwaysOnTopWindow(targetWindow)) { - predicate = function(window) { - return (isAlwaysOnTopWindow(window) && !isModalWindow(window)); - } - zBasis = zLevels.top - } else { - predicate = function(window) { - return (!isAlwaysOnTopWindow(window) && !isModalWindow(window)); - } - zBasis = zLevels.normal - } - - var windows = getTopLevelWindows(predicate); - fixupZOrder(windows, zBasis, targetWindow); - } - - Component.onCompleted: { - //offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged); - focusHack.start(); - } - - function onWindowFocusChanged() { - //console.log("Focus item is " + offscreenWindow.activeFocusItem); - - // FIXME this needs more testing before it can go into production - // and I already cant produce any way to have a modal dialog lose focus - // to a non-modal one. - /* - var focusedWindow = getDesktopWindow(offscreenWindow.activeFocusItem); - - if (isModalWindow(focusedWindow)) { - return; - } - - // new focused window is not modal... check if there are any modal windows - var windows = getTopLevelWindows(isModalWindow); - if (0 === windows.length) { - return; - } - - // There are modal windows present, force focus back to the top-most modal window - windows.sort(function(a, b){ return a.z - b.z; }); - windows[windows.length - 1].focus = true; - */ - -// var focusedItem = offscreenWindow.activeFocusItem ; -// if (DebugQML && focusedItem) { -// var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height); -// focusDebugger.x = rect.x; -// focusDebugger.y = rect.y; -// focusDebugger.width = rect.width -// focusDebugger.height = rect.height -// } - } - - function getRepositionChildren(predicate) { - return findMatchingChildren(desktop, function(child) { - return (child.shouldReposition === true && (!predicate || predicate(child))); - }); - } - - function repositionAll() { - if (desktop.repositionLocked) { - return; - } - - var oldRecommendedRect = recommendedRect; - var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height }; - var newRecommendedRect = Controller.getRecommendedHUDRect(); - var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height }; - var windows = d.getTopLevelWindows(); - for (var i = 0; i < windows.length; ++i) { - var targetWindow = windows[i]; - if (targetWindow.visible) { - repositionWindow(targetWindow, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions); - } - } - - // also reposition the other children that aren't top level windows but want to be repositioned - var otherChildren = d.getRepositionChildren(); - for (var i = 0; i < otherChildren.length; ++i) { - var child = otherChildren[i]; - repositionWindow(child, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions); - } - - } - } - - property bool pinned: false - property var hiddenChildren: [] - - function togglePinned() { - pinned = !pinned - } - - function isPointOnWindow(point) { - for (var i = 0; i < desktop.visibleChildren.length; i++) { - var child = desktop.visibleChildren[i]; - if (child.hasOwnProperty("modality")) { - var mappedPoint = mapToItem(child, point.x, point.y); - if (child.hasOwnProperty("frame")) { - var outLine = child.frame.children[2]; - var framePoint = outLine.mapFromGlobal(point.x, point.y); - if (outLine.contains(framePoint)) { - return true; - } - } - - if (child.contains(mappedPoint)) { - return true; - } - } - } - return false; - } - - function setPinned(newPinned) { - pinned = newPinned - } - - property real unpinnedAlpha: 1.0; - - Behavior on unpinnedAlpha { - NumberAnimation { - easing.type: Easing.Linear; - duration: 300 - } - } - - state: "NORMAL" - states: [ - State { - name: "NORMAL" - PropertyChanges { target: desktop; unpinnedAlpha: 1.0 } - }, - State { - name: "PINNED" - PropertyChanges { target: desktop; unpinnedAlpha: 0.0 } - } - ] - - transitions: [ - Transition { - NumberAnimation { properties: "unpinnedAlpha"; duration: 300 } - } - ] - - onPinnedChanged: { - if (pinned) { - d.raiseWindow(desktop); - desktop.focus = true; - desktop.forceActiveFocus(); - - // recalculate our non-pinned children - hiddenChildren = d.findMatchingChildren(desktop, function(child){ - return !d.isTopLevelWindow(child) && child.visible && !child.pinned; - }); - - hiddenChildren.forEach(function(child){ - child.opacity = Qt.binding(function(){ return desktop.unpinnedAlpha }); - }); - } - state = pinned ? "PINNED" : "NORMAL" - } - - onShowDesktop: pinned = false - - function raise(item) { - var targetWindow = d.getDesktopWindow(item); - if (!targetWindow) { - console.warn("Could not find top level window for " + item); - return; - } - - // Fix up the Z-order (takes into account if this is a modal window) - d.raiseWindow(targetWindow); - var setFocus = true; - if (!d.isModalWindow(targetWindow)) { - var modalWindows = d.getTopLevelWindows(d.isModalWindow); - if (modalWindows.length) { - setFocus = false; - } - } - - if (setFocus) { - targetWindow.focus = true; - } - - showDesktop(); - } - - function ensureTitleBarVisible(targetWindow) { - // Reposition window to ensure that title bar is vertically inside window. - if (targetWindow.frame && targetWindow.frame.decoration) { - var topMargin = -targetWindow.frame.decoration.anchors.topMargin; // Frame's topMargin is a negative value. - targetWindow.y = Math.max(targetWindow.y, topMargin); - } - } - - function centerOnVisible(item) { - var targetWindow = d.getDesktopWindow(item); - if (!targetWindow) { - console.warn("Could not find top level window for " + item); - return; - } - - if (typeof Controller === "undefined") { - console.warn("Controller not yet available... can't center"); - return; - } - - var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect(); - var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y, - newRecommendedRectJS.width, - newRecommendedRectJS.height); - var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height }; - var newX = newRecommendedRect.x + ((newRecommendedRect.width - targetWindow.width) / 2); - var newY = newRecommendedRect.y + ((newRecommendedRect.height - targetWindow.height) / 2); - targetWindow.x = newX; - targetWindow.y = newY; - - ensureTitleBarVisible(targetWindow); - - // If we've noticed that our recommended desktop rect has changed, record that change here. - if (recommendedRect != newRecommendedRect) { - recommendedRect = newRecommendedRect; - } - } - - function repositionOnVisible(item) { - var targetWindow = d.getDesktopWindow(item); - if (!targetWindow) { - console.warn("Could not find top level window for " + item); - return; - } - - if (typeof Controller === "undefined") { - console.warn("Controller not yet available... can't reposition targetWindow:" + targetWindow); - return; - } - - var oldRecommendedRect = recommendedRect; - var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height }; - var newRecommendedRect = Controller.getRecommendedHUDRect(); - var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height }; - repositionWindow(targetWindow, false, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions); - } - - function repositionWindow(targetWindow, forceReposition, - oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions) { - - if (desktop.width === 0 || desktop.height === 0) { - return; - } - - if (!targetWindow) { - console.warn("Could not find top level window for " + item); - return; - } - - var recommended = Controller.getRecommendedHUDRect(); - var maxX = recommended.x + recommended.width; - var maxY = recommended.y + recommended.height; - var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y); - - // if we asked to force reposition, or if the window is completely outside of the recommended rectangle, reposition it - if (forceReposition || (targetWindow.x > maxX || (targetWindow.x + targetWindow.width) < recommended.x) || - (targetWindow.y > maxY || (targetWindow.y + targetWindow.height) < recommended.y)) { - newPosition.x = -1 - newPosition.y = -1 - } - - if (newPosition.x === -1 && newPosition.y === -1) { - var originRelativeX = (targetWindow.x - oldRecommendedRect.x); - var originRelativeY = (targetWindow.y - oldRecommendedRect.y); - if (isNaN(originRelativeX)) { - originRelativeX = 0; - } - if (isNaN(originRelativeY)) { - originRelativeY = 0; - } - var fractionX = Utils.clamp(originRelativeX / oldRecommendedDimmensions.x, 0, 1); - var fractionY = Utils.clamp(originRelativeY / oldRecommendedDimmensions.y, 0, 1); - var newX = (fractionX * newRecommendedDimmensions.x) + newRecommendedRect.x; - var newY = (fractionY * newRecommendedDimmensions.y) + newRecommendedRect.y; - newPosition = Qt.vector2d(newX, newY); - } - targetWindow.x = newPosition.x; - targetWindow.y = newPosition.y; - - ensureTitleBarVisible(targetWindow); - } - - Component { id: messageDialogBuilder; MessageDialog { } } - function messageBox(properties) { - return messageDialogBuilder.createObject(desktop, properties); - } - - Component { id: inputDialogBuilder; QueryDialog { } } - function inputDialog(properties) { - return inputDialogBuilder.createObject(desktop, properties); - } - - Component { id: customInputDialogBuilder; CustomQueryDialog { } } - function customInputDialog(properties) { - return customInputDialogBuilder.createObject(desktop, properties); - } - - Component { id: fileDialogBuilder; FileDialog { } } - function fileDialog(properties) { - return fileDialogBuilder.createObject(desktop, properties); - } - - Component { id: assetDialogBuilder; AssetDialog { } } - function assetDialog(properties) { - return assetDialogBuilder.createObject(desktop, properties); - } - - function unfocusWindows() { - // First find the active focus item, and unfocus it, all the way - // up the parent chain to the window - var currentFocus = offscreenWindow.activeFocusItem; - var targetWindow = d.getDesktopWindow(currentFocus); - while (currentFocus) { - if (currentFocus === targetWindow) { - break; - } - currentFocus.focus = false; - currentFocus = currentFocus.parent; - } - - // Unfocus all windows - var windows = d.getTopLevelWindows(); - for (var i = 0; i < windows.length; ++i) { - windows[i].focus = false; - } - - // For the desktop to have active focus - desktop.focus = true; - desktop.forceActiveFocus(); - } - - function openBrowserWindow(request, profile) { - var component = Qt.createComponent("../Browser.qml"); - var newWindow = component.createObject(desktop); - newWindow.webView.profile = profile; - request.openIn(newWindow.webView); - } - - FocusHack { id: focusHack; } - - Rectangle { - id: focusDebugger; - objectName: "focusDebugger" - z: 9999; visible: false; color: "red" - ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 } - } - - Action { - text: "Toggle Focus Debugger" - shortcut: "Ctrl+Shift+F" - enabled: DebugQML - onTriggered: focusDebugger.visible = !focusDebugger.visible - } - -} diff --git a/interface/resources/qml/dialogs/+android/CustomQueryDialog.qml b/interface/resources/qml/dialogs/+android/CustomQueryDialog.qml deleted file mode 100644 index aadd7c88ae..0000000000 --- a/interface/resources/qml/dialogs/+android/CustomQueryDialog.qml +++ /dev/null @@ -1,338 +0,0 @@ -// -// CustomQueryDialog.qml -// -// Created by Zander Otavka on 7/14/16 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.7; -import QtQuick.Dialogs 1.2 as OriginalDialogs; -import QtQuick.Controls 1.4; - -import "../controls-uit"; -import "../styles-uit"; -import "../windows"; - -ModalWindow { - id: root; - HifiConstants { id: hifi; } - implicitWidth: 640; - implicitHeight: 320; - visible: true; - keyboardOverride: true // Disable ModalWindow's keyboard. - - signal selected(var result); - signal canceled(); - - property int icon: hifi.icons.none; - property string iconText: ""; - property int iconSize: 35; - onIconChanged: updateIcon(); - - property var textInput; - property var comboBox; - property var checkBox; - onTextInputChanged: { - if (textInput && textInput.text !== undefined) { - textField.text = textInput.text; - } - } - onComboBoxChanged: { - if (comboBox && comboBox.index !== undefined) { - comboBoxField.currentIndex = comboBox.index; - } - } - onCheckBoxChanged: { - if (checkBox && checkBox.checked !== undefined) { - checkBoxField.checked = checkBox.checked; - } - } - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - onKeyboardRaisedChanged: d.resize(); - - property var warning: ""; - property var result; - - property var implicitCheckState: null; - - property int titleWidth: 0; - onTitleWidthChanged: d.resize(); - - function updateIcon() { - if (!root) { - return; - } - iconText = hifi.glyphForIcon(root.icon); - } - - function updateCheckbox() { - if (checkBox.disableForItems) { - var currentItemInDisableList = false; - for (var i in checkBox.disableForItems) { - if (comboBoxField.currentIndex === checkBox.disableForItems[i]) { - currentItemInDisableList = true; - break; - } - } - - if (currentItemInDisableList) { - checkBoxField.enabled = false; - if (checkBox.checkStateOnDisable !== null && checkBox.checkStateOnDisable !== undefined) { - root.implicitCheckState = checkBoxField.checked; - checkBoxField.checked = checkBox.checkStateOnDisable; - } - root.warning = checkBox.warningOnDisable; - } else { - checkBoxField.enabled = true; - if (root.implicitCheckState !== null) { - checkBoxField.checked = root.implicitCheckState; - root.implicitCheckState = null; - } - root.warning = ""; - } - } - } - - Item { - clip: true; - width: pane.width; - height: pane.height; - anchors.margins: 0; - - QtObject { - id: d; - readonly property int minWidth: 480 - readonly property int maxWdith: 1280 - readonly property int minHeight: 120 - readonly property int maxHeight: 720 - - function resize() { - var targetWidth = Math.max(titleWidth, pane.width); - var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) + - (extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) + - (buttons.height + 3 * hifi.dimensions.contentSpacing.y) + - ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + hifi.dimensions.contentSpacing.y) : 0); - - root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth); - root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? - d.maxHeight : targetHeight); - } - } - - Item { - anchors { - top: parent.top; - bottom: extraInputs.visible ? extraInputs.top : buttons.top; - left: parent.left; - right: parent.right; - margins: 0; - } - - // FIXME make a text field type that can be bound to a history for autocompletion - TextField { - id: textField; - label: root.textInput.label; - focus: root.textInput ? true : false; - visible: root.textInput ? true : false; - anchors { - left: parent.left; - right: parent.right; - bottom: keyboard.top; - bottomMargin: hifi.dimensions.contentSpacing.y; - } - } - - Keyboard { - id: keyboard - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - bottomMargin: raised ? hifi.dimensions.contentSpacing.y : 0 - } - } - } - - Item { - id: extraInputs; - visible: Boolean(root.checkBox || root.comboBox); - anchors { - left: parent.left; - right: parent.right; - bottom: buttons.top; - bottomMargin: hifi.dimensions.contentSpacing.y; - } - height: comboBoxField.controlHeight; - onHeightChanged: d.resize(); - onWidthChanged: d.resize(); - - CheckBox { - id: checkBoxField; - text: root.checkBox.label; - focus: Boolean(root.checkBox); - visible: Boolean(root.checkBox); - anchors { - left: parent.left; - bottom: parent.bottom; - leftMargin: 6; // Magic number to align with warning icon - bottomMargin: 6; - } - } - - ComboBox { - id: comboBoxField; - label: root.comboBox.label; - focus: Boolean(root.comboBox); - visible: Boolean(root.comboBox); - Binding on x { - when: comboBoxField.visible - value: !checkBoxField.visible ? buttons.x : acceptButton.x - } - - Binding on width { - when: comboBoxField.visible - value: !checkBoxField.visible ? buttons.width : buttons.width - acceptButton.x - } - anchors { - right: parent.right; - bottom: parent.bottom; - } - model: root.comboBox ? root.comboBox.items : []; - onAccepted: { - updateCheckbox(); - focus = true; - } - } - } - - Row { - id: buttons; - focus: true; - spacing: hifi.dimensions.contentSpacing.x; - layoutDirection: Qt.RightToLeft; - onHeightChanged: d.resize(); - onWidthChanged: { - d.resize(); - resizeWarningText(); - } - - anchors { - bottom: parent.bottom; - left: parent.left; - right: parent.right; - bottomMargin: hifi.dimensions.contentSpacing.y; - } - - function resizeWarningText() { - var rowWidth = buttons.width; - var buttonsWidth = acceptButton.width + cancelButton.width + hifi.dimensions.contentSpacing.x * 2; - var warningIconWidth = warningIcon.width + hifi.dimensions.contentSpacing.x; - warningText.width = rowWidth - buttonsWidth - warningIconWidth; - } - - Button { - id: cancelButton; - action: cancelAction; - } - - Button { - id: acceptButton; - action: acceptAction; - } - - Text { - id: warningText; - visible: Boolean(root.warning); - text: root.warning; - wrapMode: Text.WordWrap; - font.italic: true; - maximumLineCount: 3; - } - - HiFiGlyphs { - id: warningIcon; - visible: Boolean(root.warning); - text: hifi.glyphs.alert; - size: hifi.dimensions.controlLineHeight; - width: 20 // Line up with checkbox. - } - } - - Action { - id: cancelAction; - text: qsTr("Cancel"); - shortcut: "Esc"; - onTriggered: { - root.result = null; - root.canceled(); - // FIXME we are leaking memory to avoid a crash - // root.destroy(); - - root.disableFade = true - visible = false; - } - } - - Action { - id: acceptAction; - text: qsTr("Add"); - shortcut: "Return" - onTriggered: { - var result = {}; - if (textInput) { - result.textInput = textField.text; - } - if (comboBox) { - result.comboBox = comboBoxField.currentIndex; - result.comboBoxText = comboBoxField.currentText; - } - if (checkBox) { - result.checkBox = checkBoxField.enabled ? checkBoxField.checked : null; - } - root.result = JSON.stringify(result); - root.selected(root.result); - // FIXME we are leaking memory to avoid a crash - // root.destroy(); - - root.disableFade = true - visible = false; - } - } - } - - Keys.onPressed: { - if (!visible) { - return; - } - - switch (event.key) { - case Qt.Key_Escape: - case Qt.Key_Back: - cancelAction.trigger(); - event.accepted = true; - break; - - case Qt.Key_Return: - case Qt.Key_Enter: - acceptAction.trigger(); - event.accepted = true; - break; - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - updateIcon(); - updateCheckbox(); - d.resize(); - textField.forceActiveFocus(); - } -} diff --git a/interface/resources/qml/dialogs/+android/FileDialog.qml b/interface/resources/qml/dialogs/+android/FileDialog.qml deleted file mode 100644 index be6524d2b8..0000000000 --- a/interface/resources/qml/dialogs/+android/FileDialog.qml +++ /dev/null @@ -1,840 +0,0 @@ -// -// FileDialog.qml -// -// Created by Bradley Austin Davis on 14 Jan 2016 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.7 -import Qt.labs.folderlistmodel 2.1 -import Qt.labs.settings 1.0 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import QtQuick.Controls 1.4 - -import ".." -import "../controls-uit" -import "../styles-uit" -import "../windows" - -import "fileDialog" - -//FIXME implement shortcuts for favorite location -ModalWindow { - id: root - resizable: true - implicitWidth: 480 - implicitHeight: 360 + (fileDialogItem.keyboardEnabled && fileDialogItem.keyboardRaised ? keyboard.raisedHeight + hifi.dimensions.contentSpacing.y : 0) - - minSize: Qt.vector2d(360, 240) - draggable: true - - HifiConstants { id: hifi } - - property var filesModel: ListModel { } - - Settings { - category: "FileDialog" - property alias width: root.width - property alias height: root.height - property alias x: root.x - property alias y: root.y - } - - - // Set from OffscreenUi::getOpenFile() - property alias caption: root.title; - // Set from OffscreenUi::getOpenFile() - property alias dir: fileTableModel.folder; - // Set from OffscreenUi::getOpenFile() - property alias filter: selectionType.filtersString; - // Set from OffscreenUi::getOpenFile() - property int options; // <-- FIXME unused - - property string iconText: root.title !== "" ? hifi.glyphs.scriptUpload : "" - property int iconSize: 40 - - property bool selectDirectory: false; - property bool showHidden: true; - // FIXME implement - property bool multiSelect: false; - property bool saveDialog: false; - property var helper: fileDialogHelper - property alias model: fileTableView.model - property var drives: helper.drives() - - property int titleWidth: 0 - - signal selectedFile(var file); - signal canceled(); - signal selected(int button); - function click(button) { - clickedButton = button; - selected(button); - destroy(); - } - - property int clickedButton: OriginalDialogs.StandardButton.NoButton; - - Component.onCompleted: { - console.log("Helper " + helper + " drives " + drives); - - fileDialogItem.keyboardEnabled = HMD.active; - - // HACK: The following lines force the model to initialize properly such that the go-up button - // works properly from the initial screen. - var initialFolder = folderListModel.folder; - fileTableModel.folder = helper.pathToUrl(drives[0]); - fileTableModel.folder = initialFolder; - - iconText = root.title !== "" ? hifi.glyphs.scriptUpload : ""; - - // Clear selection when click on external frame. - frameClicked.connect(function() { d.clearSelection(); }); - - if (selectDirectory) { - currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder)); - d.currentSelectionIsFolder = true; - d.currentSelectionUrl = initialFolder; - } - - helper.contentsChanged.connect(function() { - if (folderListModel) { - // Make folderListModel refresh. - var save = folderListModel.folder; - folderListModel.folder = ""; - folderListModel.folder = save; - } - }); - - focusTimer.start(); - } - - Timer { - id: focusTimer - interval: 10 - running: false - repeat: false - onTriggered: { - fileTableView.contentItem.forceActiveFocus(); - } - } - - Item { - id: fileDialogItem - clip: true - width: pane.width - height: pane.height - anchors.margins: 0 - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - MouseArea { - // Clear selection when click on internal unused area. - anchors.fill: parent - drag.target: root - onClicked: { - d.clearSelection(); - // Defocus text field so that the keyboard gets hidden. - // Clicking also breaks keyboard navigation apart from backtabbing to cancel - frame.forceActiveFocus(); - } - } - - Row { - id: navControls - anchors { - top: parent.top - topMargin: hifi.dimensions.contentMargin.y - left: parent.left - } - spacing: hifi.dimensions.contentSpacing.x - - GlyphButton { - id: upButton - glyph: hifi.glyphs.levelUp - width: height - size: 30 - enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" - onClicked: d.navigateUp(); - Keys.onReturnPressed: { d.navigateUp(); } - KeyNavigation.tab: homeButton - KeyNavigation.backtab: upButton - KeyNavigation.left: upButton - KeyNavigation.right: homeButton - } - - GlyphButton { - id: homeButton - property var destination: helper.home(); - glyph: hifi.glyphs.home - size: 28 - width: height - enabled: d.homeDestination ? true : false - onClicked: d.navigateHome(); - Keys.onReturnPressed: { d.navigateHome(); } - KeyNavigation.tab: fileTableView.contentItem - KeyNavigation.backtab: upButton - KeyNavigation.left: upButton - } - } - - ComboBox { - id: pathSelector - anchors { - top: parent.top - topMargin: hifi.dimensions.contentMargin.y - left: navControls.right - leftMargin: hifi.dimensions.contentSpacing.x - right: parent.right - } - - property var lastValidFolder: helper.urlToPath(fileTableModel.folder) - - function calculatePathChoices(folder) { - var folders = folder.split("/"), - choices = [], - i, length; - - if (folders[folders.length - 1] === "") { - folders.pop(); - } - - choices.push(folders[0]); - - for (i = 1, length = folders.length; i < length; i++) { - choices.push(choices[i - 1] + "/" + folders[i]); - } - - if (folders[0] === "") { - // Special handling for OSX root dir. - choices[0] = "/"; - } - - choices.reverse(); - - if (drives && drives.length > 1) { - choices.push("This PC"); - } - - if (choices.length > 0) { - pathSelector.model = choices; - } - } - - onLastValidFolderChanged: { - var folder = d.capitalizeDrive(lastValidFolder); - calculatePathChoices(folder); - } - - onCurrentTextChanged: { - var folder = currentText; - - if (/^[a-zA-z]:$/.test(folder)) { - folder = "file:///" + folder + "/"; - } else if (folder === "This PC") { - folder = "file:///"; - } else { - folder = helper.pathToUrl(folder); - } - - if (helper.urlToPath(folder).toLowerCase() !== helper.urlToPath(fileTableModel.folder).toLowerCase()) { - if (root.selectDirectory) { - currentSelection.text = currentText !== "This PC" ? currentText : ""; - d.currentSelectionUrl = helper.pathToUrl(currentText); - } - fileTableModel.folder = folder; - } - } - - KeyNavigation.up: fileTableView.contentItem - KeyNavigation.down: fileTableView.contentItem - KeyNavigation.tab: fileTableView.contentItem - KeyNavigation.backtab: fileTableView.contentItem - KeyNavigation.left: fileTableView.contentItem - KeyNavigation.right: fileTableView.contentItem - } - - QtObject { - id: d - property var currentSelectionUrl; - readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl); - property bool currentSelectionIsFolder; - property var backStack: [] - property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); } - property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); } - property var homeDestination: helper.home(); - - function capitalizeDrive(path) { - // Consistently capitalize drive letter for Windows. - if (/[a-zA-Z]:/.test(path)) { - return path.charAt(0).toUpperCase() + path.slice(1); - } - return path; - } - - function update() { - var row = fileTableView.currentRow; - - if (row === -1) { - if (!root.selectDirectory) { - currentSelection.text = ""; - currentSelectionIsFolder = false; - } - return; - } - - currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); - currentSelectionIsFolder = fileTableView.model !== filesModel ? - fileTableView.model.isFolder(row) : - fileTableModel.isFolder(row); - if (root.selectDirectory || !currentSelectionIsFolder) { - currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); - } else { - currentSelection.text = ""; - } - } - - function navigateUp() { - if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") { - fileTableModel.folder = fileTableModel.parentFolder; - return true; - } - } - - function navigateHome() { - fileTableModel.folder = homeDestination; - return true; - } - - function clearSelection() { - fileTableView.selection.clear(); - fileTableView.currentRow = -1; - update(); - } - } - - FolderListModel { - id: folderListModel - nameFilters: selectionType.currentFilter - showDirsFirst: true - showDotAndDotDot: false - showFiles: !root.selectDirectory - showHidden: root.showHidden - Component.onCompleted: { - showFiles = !root.selectDirectory - showHidden = root.showHidden - } - - onFolderChanged: { - d.clearSelection(); - fileTableModel.update(); // Update once the data from the folder change is available. - } - - function getItem(index, field) { - return get(index, field); - } - } - - ListModel { - // Emulates FolderListModel but contains drive data. - id: driveListModel - - property int count: 1 - - Component.onCompleted: initialize(); - - function initialize() { - var drive, - i; - - count = drives.length; - - for (i = 0; i < count; i++) { - drive = drives[i].slice(0, -1); // Remove trailing "/". - append({ - fileName: drive, - fileModified: new Date(0), - fileSize: 0, - filePath: drive + "/", - fileIsDir: true, - fileNameSort: drive.toLowerCase() - }); - } - } - - function getItem(index, field) { - return get(index)[field]; - } - } - - Component { - id: filesModelBuilder - ListModel { } - } - - QtObject { - id: fileTableModel - - // FolderListModel has a couple of problems: - // 1) Files and directories sort case-sensitively: https://bugreports.qt.io/browse/QTBUG-48757 - // 2) Cannot browse up to the "computer" level to view Windows drives: https://bugreports.qt.io/browse/QTBUG-42901 - // - // To solve these problems an intermediary ListModel is used that implements proper sorting and can be populated with - // drive information when viewing at the computer level. - - property var folder - property int sortOrder: Qt.AscendingOrder - property int sortColumn: 0 - property var model: folderListModel - property string parentFolder: calculateParentFolder(); - - readonly property string rootFolder: "file:///" - - function calculateParentFolder() { - if (model === folderListModel) { - if (folderListModel.parentFolder.toString() === "" && driveListModel.count > 1) { - return rootFolder; - } else { - return folderListModel.parentFolder; - } - } else { - return ""; - } - } - - onFolderChanged: { - if (folder === rootFolder) { - model = driveListModel; - helper.monitorDirectory(""); - update(); - } else { - var needsUpdate = model === driveListModel && folder === folderListModel.folder; - - model = folderListModel; - folderListModel.folder = folder; - helper.monitorDirectory(helper.urlToPath(folder)); - - if (needsUpdate) { - update(); - } - } - } - - function isFolder(row) { - if (row === -1) { - return false; - } - return filesModel.get(row).fileIsDir; - } - - function get(row) { - return filesModel.get(row) - } - - function update() { - var dataFields = ["fileName", "fileModified", "fileSize"], - sortFields = ["fileNameSort", "fileModified", "fileSize"], - dataField = dataFields[sortColumn], - sortField = sortFields[sortColumn], - sortValue, - fileName, - fileIsDir, - comparisonFunction, - lower, - middle, - upper, - rows = 0, - i; - - filesModel = filesModelBuilder.createObject(root); - - comparisonFunction = sortOrder === Qt.AscendingOrder - ? function(a, b) { return a < b; } - : function(a, b) { return a > b; } - - for (i = 0; i < model.count; i++) { - fileName = model.getItem(i, "fileName"); - fileIsDir = model.getItem(i, "fileIsDir"); - - sortValue = model.getItem(i, dataField); - if (dataField === "fileName") { - // Directories first by prefixing a "*". - // Case-insensitive. - sortValue = (fileIsDir ? "*" : "") + sortValue.toLowerCase(); - } - - lower = 0; - upper = rows; - while (lower < upper) { - middle = Math.floor((lower + upper) / 2); - var lessThan; - if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) { - lessThan = true; - upper = middle; - } else { - lessThan = false; - lower = middle + 1; - } - } - - filesModel.insert(lower, { - fileName: fileName, - fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), - fileSize: model.getItem(i, "fileSize"), - filePath: model.getItem(i, "filePath"), - fileIsDir: fileIsDir, - fileNameSort: (fileIsDir ? "*" : "") + fileName.toLowerCase() - }); - - rows++; - } - } - } - - Table { - id: fileTableView - colorScheme: hifi.colorSchemes.light - anchors { - top: navControls.bottom - topMargin: hifi.dimensions.contentSpacing.y - left: parent.left - right: parent.right - bottom: currentSelection.top - bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height - } - headerVisible: !selectDirectory - onDoubleClicked: navigateToRow(row); - Keys.onReturnPressed: navigateToCurrentRow(); - Keys.onEnterPressed: navigateToCurrentRow(); - - sortIndicatorColumn: 0 - sortIndicatorOrder: Qt.AscendingOrder - sortIndicatorVisible: true - - model: filesModel - - function updateSort() { - fileTableModel.sortOrder = sortIndicatorOrder; - fileTableModel.sortColumn = sortIndicatorColumn; - fileTableModel.update(); - } - - onSortIndicatorColumnChanged: { updateSort(); } - - onSortIndicatorOrderChanged: { updateSort(); } - - itemDelegate: Item { - clip: true - - FiraSansSemiBold { - text: getText(); - elide: styleData.elideMode - anchors { - left: parent.left - leftMargin: hifi.dimensions.tablePadding - right: parent.right - rightMargin: hifi.dimensions.tablePadding - verticalCenter: parent.verticalCenter - } - size: hifi.fontSizes.tableText - color: hifi.colors.baseGrayHighlight - font.family: (styleData.row !== -1 && fileTableView.model.get(styleData.row).fileIsDir) - ? "Fira Sans SemiBold" : "Fira Sans" - - function getText() { - if (styleData.row === -1) { - return styleData.value; - } - - switch (styleData.column) { - case 1: return fileTableView.model.get(styleData.row).fileIsDir ? "" : styleData.value; - case 2: return fileTableView.model.get(styleData.row).fileIsDir ? "" : formatSize(styleData.value); - default: return styleData.value; - } - } - function formatSize(size) { - var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ]; - var suffixIndex = 0 - while ((size / 1024.0) > 1.1) { - size /= 1024.0; - ++suffixIndex; - } - - size = Math.round(size*1000)/1000; - size = size.toLocaleString() - - return size + " " + suffixes[suffixIndex]; - } - } - } - - TableViewColumn { - id: fileNameColumn - role: "fileName" - title: "Name" - width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width - movable: false - resizable: true - } - TableViewColumn { - id: fileModifiedColumn - role: "fileModified" - title: "Date" - width: 0.3 * fileTableView.width - movable: false - resizable: true - visible: !selectDirectory - } - TableViewColumn { - role: "fileSize" - title: "Size" - width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width - movable: false - resizable: true - visible: !selectDirectory - } - - function navigateToRow(row) { - currentRow = row; - navigateToCurrentRow(); - } - - function navigateToCurrentRow() { - var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel - var row = fileTableView.currentRow - var isFolder = currentModel.isFolder(row); - var file = currentModel.get(row).filePath; - if (isFolder) { - currentModel.folder = helper.pathToUrl(file); - } else { - okAction.trigger(); - } - } - - property string prefix: "" - - function addToPrefix(event) { - if (!event.text || event.text === "") { - return false; - } - var newPrefix = prefix + event.text.toLowerCase(); - var matchedIndex = -1; - for (var i = 0; i < model.count; ++i) { - var name = model !== filesModel ? model.get(i).fileName.toLowerCase() : - filesModel.get(i).fileName.toLowerCase(); - if (0 === name.indexOf(newPrefix)) { - matchedIndex = i; - break; - } - } - - if (matchedIndex !== -1) { - fileTableView.selection.clear(); - fileTableView.selection.select(matchedIndex); - fileTableView.currentRow = matchedIndex; - fileTableView.prefix = newPrefix; - } - prefixClearTimer.restart(); - return true; - } - - Timer { - id: prefixClearTimer - interval: 1000 - repeat: false - running: false - onTriggered: fileTableView.prefix = ""; - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Backspace: - case Qt.Key_Tab: - case Qt.Key_Backtab: - event.accepted = false; - break; - case Qt.Key_Escape: - event.accepted = true; - root.click(OriginalDialogs.StandardButton.Cancel); - break; - default: - if (addToPrefix(event)) { - event.accepted = true - } else { - event.accepted = false; - } - break; - } - } - - KeyNavigation.tab: root.saveDialog ? currentSelection : openButton - } - - TextField { - id: currentSelection - label: selectDirectory ? "Directory:" : "File name:" - anchors { - left: parent.left - right: selectionType.visible ? selectionType.left: parent.right - rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0 - bottom: keyboard.top - bottomMargin: hifi.dimensions.contentSpacing.y - } - readOnly: !root.saveDialog - activeFocusOnTab: !readOnly - onActiveFocusChanged: if (activeFocus) { selectAll(); } - onAccepted: okAction.trigger(); - KeyNavigation.up: fileTableView.contentItem - KeyNavigation.down: openButton - KeyNavigation.tab: openButton - KeyNavigation.backtab: fileTableView.contentItem - } - - FileTypeSelection { - id: selectionType - anchors { - top: currentSelection.top - left: buttonRow.left - right: parent.right - } - visible: !selectDirectory && filtersCount > 1 - } - - Keyboard { - id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttonRow.top - bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0 - } - } - - Row { - id: buttonRow - anchors { - right: parent.right - bottom: parent.bottom - } - spacing: hifi.dimensions.contentSpacing.y - - Button { - id: openButton - color: hifi.buttons.blue - action: okAction - Keys.onReturnPressed: okAction.trigger() - KeyNavigation.right: cancelButton - KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem - KeyNavigation.tab: cancelButton - } - - Button { - id: cancelButton - action: cancelAction - Keys.onReturnPressed: { cancelAction.trigger() } - KeyNavigation.left: openButton - KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem - KeyNavigation.backtab: openButton - } - } - - Action { - id: okAction - text: currentSelection.text ? (root.selectDirectory && fileTableView.currentRow === -1 ? "Choose" : (root.saveDialog ? "Save" : "Open")) : "Open" - enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false - onTriggered: { - if (!root.selectDirectory && !d.currentSelectionIsFolder - || root.selectDirectory && fileTableView.currentRow === -1) { - okActionTimer.start(); - } else { - fileTableView.navigateToCurrentRow(); - } - } - } - - Timer { - id: okActionTimer - interval: 50 - running: false - repeat: false - onTriggered: { - if (!root.saveDialog) { - selectedFile(d.currentSelectionUrl); - root.destroy() - return; - } - - // Handle the ambiguity between different cases - // * typed name (with or without extension) - // * full path vs relative vs filename only - var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter); - - if (!selection) { - desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" }) - return; - } - - if (helper.urlIsDir(selection)) { - root.dir = selection; - currentSelection.text = ""; - return; - } - - // Check if the file is a valid target - if (!helper.urlIsWritable(selection)) { - desktop.messageBox({ - icon: OriginalDialogs.StandardIcon.Warning, - text: "Unable to write to location " + selection - }) - return; - } - - if (helper.urlExists(selection)) { - var messageBox = desktop.messageBox({ - icon: OriginalDialogs.StandardIcon.Question, - buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, - text: "Do you wish to overwrite " + selection + "?", - }); - var result = messageBox.exec(); - if (OriginalDialogs.StandardButton.Yes !== result) { - return; - } - } - - console.log("Selecting " + selection) - selectedFile(selection); - root.destroy(); - } - } - - Action { - id: cancelAction - text: "Cancel" - onTriggered: { canceled(); root.shown = false; } - } - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Backspace: - event.accepted = d.navigateUp(); - break; - - case Qt.Key_Home: - event.accepted = d.navigateHome(); - break; - - case Qt.Key_Escape: - event.accepted = true; - root.click(OriginalDialogs.StandardButton.Cancel); - break; - } - } -} diff --git a/interface/resources/qml/dialogs/+android/QueryDialog.qml b/interface/resources/qml/dialogs/+android/QueryDialog.qml deleted file mode 100644 index aec6d8a286..0000000000 --- a/interface/resources/qml/dialogs/+android/QueryDialog.qml +++ /dev/null @@ -1,231 +0,0 @@ -// -// QueryDialog.qml -// -// Created by Bradley Austin Davis on 22 Jan 2016 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.7 -import QtQuick.Controls 1.4 - -import "../controls-uit" -import "../styles-uit" -import "../windows" - -ModalWindow { - id: root - HifiConstants { id: hifi } - implicitWidth: 640 - implicitHeight: 320 - visible: true - keyboardOverride: true // Disable ModalWindow's keyboard. - - signal selected(var result); - signal canceled(); - - property int icon: hifi.icons.none - property string iconText: "" - property int iconSize: 35 - onIconChanged: updateIcon(); - - property var items; - property string label - property var result; - property alias current: textResult.text - - // For text boxes - property alias placeholderText: textResult.placeholderText - - // For combo boxes - property bool editable: true; - - property int titleWidth: 0 - onTitleWidthChanged: d.resize(); - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - onKeyboardRaisedChanged: d.resize(); - - function updateIcon() { - if (!root) { - return; - } - iconText = hifi.glyphForIcon(root.icon); - } - - Item { - id: modalWindowItem - clip: true - width: pane.width - height: pane.height - anchors.margins: 0 - - QtObject { - id: d - readonly property int minWidth: 480 - readonly property int maxWdith: 1280 - readonly property int minHeight: 120 - readonly property int maxHeight: 720 - - function resize() { - var targetWidth = Math.max(titleWidth, pane.width) - var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height - root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth); - root.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0) - } - } - - Item { - anchors { - top: parent.top - bottom: keyboard.top; - left: parent.left; - right: parent.right; - margins: 0 - bottomMargin: 2 * hifi.dimensions.contentSpacing.y - } - - // FIXME make a text field type that can be bound to a history for autocompletion - TextField { - id: textResult - label: root.label - visible: items ? false : true - anchors { - left: parent.left; - right: parent.right; - bottom: parent.bottom - } - KeyNavigation.down: acceptButton - KeyNavigation.tab: acceptButton - } - - ComboBox { - id: comboBox - label: root.label - visible: items ? true : false - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - model: items ? items : [] - KeyNavigation.down: acceptButton - KeyNavigation.tab: acceptButton - } - } - - property alias keyboardOverride: root.keyboardOverride - property alias keyboardRaised: root.keyboardRaised - property alias punctuationMode: root.punctuationMode - Keyboard { - id: keyboard - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttons.top - bottomMargin: raised ? 2 * hifi.dimensions.contentSpacing.y : 0 - } - } - - Flow { - id: buttons - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - layoutDirection: Qt.RightToLeft - anchors { - bottom: parent.bottom - right: parent.right - margins: 0 - bottomMargin: hifi.dimensions.contentSpacing.y - } - Button { - id: cancelButton - action: cancelAction - KeyNavigation.left: acceptButton - KeyNavigation.up: items ? comboBox : textResult - KeyNavigation.backtab: acceptButton - } - Button { - id: acceptButton - action: acceptAction - KeyNavigation.right: cancelButton - KeyNavigation.up: items ? comboBox : textResult - KeyNavigation.tab: cancelButton - KeyNavigation.backtab: items ? comboBox : textResult - } - } - - Action { - id: cancelAction - text: qsTr("Cancel"); - shortcut: "Esc" - onTriggered: { - root.canceled(); - // FIXME we are leaking memory to avoid a crash - // root.destroy(); - - root.disableFade = true - visible = false; - } - } - - Action { - id: acceptAction - text: qsTr("OK"); - shortcut: "Return" - onTriggered: { - root.result = items ? comboBox.currentText : textResult.text - root.selected(root.result); - // FIXME we are leaking memory to avoid a crash - // root.destroy(); - - root.disableFade = true - visible = false; - } - } - } - - Keys.onPressed: { - if (!visible) { - return - } - - switch (event.key) { - case Qt.Key_Escape: - case Qt.Key_Back: - cancelAction.trigger() - event.accepted = true; - break; - - case Qt.Key_Return: - case Qt.Key_Enter: - if (acceptButton.focus) { - acceptAction.trigger() - } else if (cancelButton.focus) { - cancelAction.trigger() - } else if (comboBox.focus || comboBox.popup.focus) { - comboBox.showList() - } - event.accepted = true; - break; - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - updateIcon(); - d.resize(); - if (items) { - comboBox.forceActiveFocus() - } else { - textResult.forceActiveFocus() - } - } -} diff --git a/interface/resources/qml/dialogs/assetDialog/+android/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/+android/AssetDialogContent.qml deleted file mode 100644 index 54bdb0a888..0000000000 --- a/interface/resources/qml/dialogs/assetDialog/+android/AssetDialogContent.qml +++ /dev/null @@ -1,533 +0,0 @@ -// -// AssetDialogContent.qml -// -// Created by David Rowe on 19 Apr 2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.7 -import QtQuick.Controls 1.5 - -import "../../controls-uit" -import "../../styles-uit" - -import "../fileDialog" - -Item { - // Set from OffscreenUi::assetDialog() - property alias dir: assetTableModel.folder - property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx". - property int options // Not used. - - property bool selectDirectory: false - - // Not implemented. - //property bool saveDialog: false; - //property bool multiSelect: false; - - property bool singleClickNavigate: false - - HifiConstants { id: hifi } - - Component.onCompleted: { - homeButton.destination = dir; - - if (selectDirectory) { - d.currentSelectionIsFolder = true; - d.currentSelectionPath = assetTableModel.folder; - } - - assetTableView.forceActiveFocus(); - } - - Item { - id: assetDialogItem - anchors.fill: parent - clip: true - - MouseArea { - // Clear selection when click on internal unused area. - anchors.fill: parent - drag.target: root - onClicked: { - d.clearSelection(); - frame.forceActiveFocus(); - assetTableView.forceActiveFocus(); - } - } - - Row { - id: navControls - anchors { - top: parent.top - topMargin: hifi.dimensions.contentMargin.y - left: parent.left - } - spacing: hifi.dimensions.contentSpacing.x - - GlyphButton { - id: upButton - glyph: hifi.glyphs.levelUp - width: height - size: 30 - enabled: assetTableModel.parentFolder !== "" - onClicked: d.navigateUp(); - } - - GlyphButton { - id: homeButton - property string destination: "" - glyph: hifi.glyphs.home - size: 28 - width: height - enabled: destination !== "" - //onClicked: d.navigateHome(); - onClicked: assetTableModel.folder = destination; - } - } - - ComboBox { - id: pathSelector - anchors { - top: parent.top - topMargin: hifi.dimensions.contentMargin.y - left: navControls.right - leftMargin: hifi.dimensions.contentSpacing.x - right: parent.right - } - z: 10 - - property string lastValidFolder: assetTableModel.folder - - function calculatePathChoices(folder) { - var folders = folder.split("/"), - choices = [], - i, length; - - if (folders[folders.length - 1] === "") { - folders.pop(); - } - - choices.push(folders[0]); - - for (i = 1, length = folders.length; i < length; i++) { - choices.push(choices[i - 1] + "/" + folders[i]); - } - - if (folders[0] === "") { - choices[0] = "/"; - } - - choices.reverse(); - - if (choices.length > 0) { - pathSelector.model = choices; - } - } - - onLastValidFolderChanged: { - var folder = lastValidFolder; - calculatePathChoices(folder); - } - - onCurrentTextChanged: { - var folder = currentText; - - if (folder !== "/") { - folder += "/"; - } - - if (folder !== assetTableModel.folder) { - if (root.selectDirectory) { - currentSelection.text = currentText; - d.currentSelectionPath = currentText; - } - assetTableModel.folder = folder; - assetTableView.forceActiveFocus(); - } - } - } - - QtObject { - id: d - - property string currentSelectionPath - property bool currentSelectionIsFolder - property var tableViewConnection: Connections { target: assetTableView; onCurrentRowChanged: d.update(); } - - function update() { - var row = assetTableView.currentRow; - - if (row === -1) { - if (!root.selectDirectory) { - currentSelection.text = ""; - currentSelectionIsFolder = false; - } - return; - } - - var rowInfo = assetTableModel.get(row); - currentSelectionPath = rowInfo.filePath; - currentSelectionIsFolder = rowInfo.fileIsDir; - if (root.selectDirectory || !currentSelectionIsFolder) { - currentSelection.text = currentSelectionPath; - } else { - currentSelection.text = ""; - } - } - - function navigateUp() { - if (assetTableModel.parentFolder !== "") { - assetTableModel.folder = assetTableModel.parentFolder; - return true; - } - return false; - } - - function navigateHome() { - assetTableModel.folder = homeButton.destination; - return true; - } - - function clearSelection() { - assetTableView.selection.clear(); - assetTableView.currentRow = -1; - update(); - } - } - - ListModel { - id: assetTableModel - - property string folder - property string parentFolder: "" - readonly property string rootFolder: "/" - - onFolderChanged: { - parentFolder = calculateParentFolder(); - update(); - } - - function calculateParentFolder() { - if (folder !== "/") { - return folder.slice(0, folder.slice(0, -1).lastIndexOf("/") + 1); - } - return ""; - } - - function isFolder(row) { - if (row === -1) { - return false; - } - return get(row).fileIsDir; - } - - function onGetAllMappings(error, map) { - var mappings, - fileTypeFilter, - index, - path, - fileName, - fileType, - fileIsDir, - isValid, - subDirectory, - subDirectories = [], - fileNameSort, - rows = 0, - lower, - middle, - upper, - i, - length; - - clear(); - - if (error === "") { - mappings = Object.keys(map); - fileTypeFilter = filter.replace("*", "").toLowerCase(); - - for (i = 0, length = mappings.length; i < length; i++) { - index = mappings[i].lastIndexOf("/"); - - path = mappings[i].slice(0, mappings[i].lastIndexOf("/") + 1); - fileName = mappings[i].slice(path.length); - fileType = fileName.slice(fileName.lastIndexOf(".")); - fileIsDir = false; - isValid = false; - - if (fileType.toLowerCase() === fileTypeFilter) { - if (path === folder) { - isValid = !selectDirectory; - } else if (path.length > folder.length) { - subDirectory = path.slice(folder.length); - index = subDirectory.indexOf("/"); - if (index === subDirectory.lastIndexOf("/")) { - fileName = subDirectory.slice(0, index); - if (subDirectories.indexOf(fileName) === -1) { - fileIsDir = true; - isValid = true; - subDirectories.push(fileName); - } - } - } - } - - if (isValid) { - fileNameSort = (fileIsDir ? "*" : "") + fileName.toLowerCase(); - - lower = 0; - upper = rows; - while (lower < upper) { - middle = Math.floor((lower + upper) / 2); - var lessThan; - if (fileNameSort < get(middle)["fileNameSort"]) { - lessThan = true; - upper = middle; - } else { - lessThan = false; - lower = middle + 1; - } - } - - insert(lower, { - fileName: fileName, - filePath: path + (fileIsDir ? "" : fileName), - fileIsDir: fileIsDir, - fileNameSort: fileNameSort - }); - - rows++; - } - } - - } else { - console.log("Error getting mappings from Asset Server"); - } - } - - function update() { - d.clearSelection(); - clear(); - Assets.getAllMappings(onGetAllMappings); - } - } - - Table { - id: assetTableView - colorScheme: hifi.colorSchemes.light - anchors { - top: navControls.bottom - topMargin: hifi.dimensions.contentSpacing.y - left: parent.left - right: parent.right - bottom: currentSelection.top - bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height - } - - model: assetTableModel - - focus: true - - onClicked: { - if (singleClickNavigate) { - navigateToRow(row); - } - } - - onDoubleClicked: navigateToRow(row); - Keys.onReturnPressed: navigateToCurrentRow(); - Keys.onEnterPressed: navigateToCurrentRow(); - - itemDelegate: Item { - clip: true - - FiraSansSemiBold { - text: styleData.value - elide: styleData.elideMode - anchors { - left: parent.left - leftMargin: hifi.dimensions.tablePadding - right: parent.right - rightMargin: hifi.dimensions.tablePadding - verticalCenter: parent.verticalCenter - } - size: hifi.fontSizes.tableText - color: hifi.colors.baseGrayHighlight - font.family: (styleData.row !== -1 && assetTableView.model.get(styleData.row).fileIsDir) - ? "Fira Sans SemiBold" : "Fira Sans" - } - } - - TableViewColumn { - id: fileNameColumn - role: "fileName" - title: "Name" - width: assetTableView.width - movable: false - resizable: false - } - - function navigateToRow(row) { - currentRow = row; - navigateToCurrentRow(); - } - - function navigateToCurrentRow() { - if (model.isFolder(currentRow)) { - model.folder = model.get(currentRow).filePath; - } else { - okAction.trigger(); - } - } - - Timer { - id: prefixClearTimer - interval: 1000 - repeat: false - running: false - onTriggered: assetTableView.prefix = ""; - } - - property string prefix: "" - - function addToPrefix(event) { - if (!event.text || event.text === "") { - return false; - } - var newPrefix = prefix + event.text.toLowerCase(); - var matchedIndex = -1; - for (var i = 0; i < model.count; ++i) { - var name = model.get(i).fileName.toLowerCase(); - if (0 === name.indexOf(newPrefix)) { - matchedIndex = i; - break; - } - } - - if (matchedIndex !== -1) { - assetTableView.selection.clear(); - assetTableView.selection.select(matchedIndex); - assetTableView.currentRow = matchedIndex; - assetTableView.prefix = newPrefix; - } - prefixClearTimer.restart(); - return true; - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Backspace: - case Qt.Key_Tab: - case Qt.Key_Backtab: - event.accepted = false; - break; - - default: - if (addToPrefix(event)) { - event.accepted = true - } else { - event.accepted = false; - } - break; - } - } - } - - TextField { - id: currentSelection - label: selectDirectory ? "Directory:" : "File name:" - anchors { - left: parent.left - right: selectionType.visible ? selectionType.left: parent.right - rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0 - bottom: buttonRow.top - bottomMargin: hifi.dimensions.contentSpacing.y - } - readOnly: true - activeFocusOnTab: !readOnly - onActiveFocusChanged: if (activeFocus) { selectAll(); } - onAccepted: okAction.trigger(); - } - - FileTypeSelection { - id: selectionType - anchors { - top: currentSelection.top - left: buttonRow.left - right: parent.right - } - visible: !selectDirectory && filtersCount > 1 - KeyNavigation.left: assetTableView - KeyNavigation.right: openButton - } - - Action { - id: okAction - text: currentSelection.text && root.selectDirectory && assetTableView.currentRow === -1 ? "Choose" : "Open" - enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false - onTriggered: { - if (!root.selectDirectory && !d.currentSelectionIsFolder - || root.selectDirectory && assetTableView.currentRow === -1) { - selectedAsset(d.currentSelectionPath); - root.destroy(); - } else { - assetTableView.navigateToCurrentRow(); - } - } - } - - Action { - id: cancelAction - text: "Cancel" - onTriggered: { - canceled(); - root.destroy(); - } - } - - Row { - id: buttonRow - anchors { - right: parent.right - bottom: parent.bottom - } - spacing: hifi.dimensions.contentSpacing.y - - Button { - id: openButton - color: hifi.buttons.blue - action: okAction - Keys.onReturnPressed: okAction.trigger() - KeyNavigation.up: selectionType - KeyNavigation.left: selectionType - KeyNavigation.right: cancelButton - } - - Button { - id: cancelButton - action: cancelAction - KeyNavigation.up: selectionType - KeyNavigation.left: openButton - KeyNavigation.right: assetTableView.contentItem - Keys.onReturnPressed: { canceled(); root.enabled = false } - } - } - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Backspace: - event.accepted = d.navigateUp(); - break; - - case Qt.Key_Home: - event.accepted = d.navigateHome(); - break; - - } - } -} diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 3059707313..c66ed1fe18 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -1108,7 +1108,7 @@ Rectangle { function findNearbySessionIndex(sessionId, optionalData) { // no findIndex in .qml var data = optionalData || nearbyUserModelData, length = data.length; for (var i = 0; i < length; i++) { - if (data[i].sessionId === sessionId) { + if (data[i].sessionId === sessionId.toString()) { return i; } } @@ -1120,7 +1120,7 @@ Rectangle { var data = message.params; var index = -1; iAmAdmin = Users.canKick; - index = findNearbySessionIndex('', data); + index = findNearbySessionIndex("", data); if (index !== -1) { myData = data[index]; data.splice(index, 1); @@ -1197,8 +1197,8 @@ Rectangle { for (var userId in message.params) { var audioLevel = message.params[userId][0]; var avgAudioLevel = message.params[userId][1]; - // If the userId is 0, we're updating "myData". - if (userId == 0) { + // If the userId is "", we're updating "myData". + if (userId === "") { myData.audioLevel = audioLevel; myCard.audioLevel = audioLevel; // Defensive programming myData.avgAudioLevel = avgAudioLevel; diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index a501185853..10613a9ec1 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -125,7 +125,6 @@ Rectangle { id: wearablesCombobox anchors.left: parent.left anchors.right: parent.right - comboBox.textRole: "text" model: ListModel { function findIndexById(id) { diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 653d814020..cac62d3976 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -129,7 +129,7 @@ Rectangle { } onAppInstalled: { - if (appHref === root.itemHref) { + if (appID === root.itemId) { root.isInstalled = true; } } diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 9f1d307f0e..13dc3cb37f 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -67,13 +67,13 @@ Item { } onAppInstalled: { - if (appHref === root.itemHref) { + if (appID === root.itemId) { root.isInstalled = true; } } onAppUninstalled: { - if (appHref === root.itemHref) { + if (appID === root.itemId) { root.isInstalled = false; } } @@ -328,7 +328,15 @@ Item { item.buttonColor = "#E2334D"; item.buttonClicked = function() { sendToPurchases({ method: 'flipCard', closeAll: true }); - sendToPurchases({method: 'updateItemClicked', itemId: root.itemId, itemEdition: root.itemEdition, upgradeUrl: root.upgradeUrl}); + sendToPurchases({ + method: 'updateItemClicked', + itemId: root.itemId, + itemEdition: root.itemEdition, + upgradeUrl: root.upgradeUrl, + itemHref: root.itemHref, + itemType: root.itemType, + isInstalled: root.isInstalled + }); } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 8a5b1fb0e7..91993d0fa3 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -98,7 +98,7 @@ Rectangle { } onAppInstalled: { - root.installedApps = Commerce.getInstalledApps(); + root.installedApps = Commerce.getInstalledApps(appID); } onAppUninstalled: { @@ -706,7 +706,23 @@ Rectangle { } } } else if (msg.method === "updateItemClicked") { - sendToScript(msg); + if (msg.itemType === "app" && msg.isInstalled) { + lightboxPopup.titleText = "Uninstall App"; + lightboxPopup.bodyText = "The app that you are trying to update is installed.

" + + "If you proceed, the current version of the app will be uninstalled."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + } + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = function() { + Commerce.uninstallApp(msg.itemHref); + sendToScript(msg); + }; + lightboxPopup.visible = true; + } else { + sendToScript(msg); + } } else if (msg.method === "giftAsset") { sendAsset.assetName = msg.itemName; sendAsset.assetCertID = msg.certId; diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp index 1e73d58939..419382f2cb 100644 --- a/interface/src/AndroidHelper.cpp +++ b/interface/src/AndroidHelper.cpp @@ -35,6 +35,10 @@ void AndroidHelper::notifyEnterForeground() { emit enterForeground(); } +void AndroidHelper::notifyBeforeEnterBackground() { + emit beforeEnterBackground(); +} + void AndroidHelper::notifyEnterBackground() { emit enterBackground(); } diff --git a/interface/src/AndroidHelper.h b/interface/src/AndroidHelper.h index 2c536268d6..03d92f91d9 100644 --- a/interface/src/AndroidHelper.h +++ b/interface/src/AndroidHelper.h @@ -24,6 +24,7 @@ public: void requestActivity(const QString &activityName, const bool backToScene, QList args = QList()); void notifyLoadComplete(); void notifyEnterForeground(); + void notifyBeforeEnterBackground(); void notifyEnterBackground(); void performHapticFeedback(int duration); @@ -39,6 +40,7 @@ signals: void androidActivityRequested(const QString &activityName, const bool backToScene, QList args = QList()); void qtAppLoadComplete(); void enterForeground(); + void beforeEnterBackground(); void enterBackground(); void hapticFeedbackRequested(int duration); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7e683c7cc8..311c08b858 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1448,8 +1448,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // add firstRun flag from settings to launch event Setting::Handle firstRun { Settings::firstRun, true }; - QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); - auto& userActivityLogger = UserActivityLogger::getInstance(); if (userActivityLogger.isEnabled()) { // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. @@ -1501,13 +1499,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["first_run"] = firstRun.get(); // add the user's machine ID to the launch event + QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); properties["machine_fingerprint"] = machineFingerPrint; userActivityLogger.logAction("launch", properties); } - setCrashAnnotation("machine_fingerprint", machineFingerPrint.toStdString()); - _entityEditSender.setMyAvatar(myAvatar.get()); // The entity octree will have to know about MyAvatar for the parentJointName import @@ -2261,6 +2258,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); #if defined(Q_OS_ANDROID) + connect(&AndroidHelper::instance(), &AndroidHelper::beforeEnterBackground, this, &Application::beforeEnterBackground); connect(&AndroidHelper::instance(), &AndroidHelper::enterBackground, this, &Application::enterBackground); connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); AndroidHelper::instance().notifyLoadComplete(); @@ -3223,6 +3221,7 @@ void Application::setSettingConstrainToolbarPosition(bool setting) { void Application::showHelp() { static const QString HAND_CONTROLLER_NAME_VIVE = "vive"; static const QString HAND_CONTROLLER_NAME_OCULUS_TOUCH = "oculus"; + static const QString HAND_CONTROLLER_NAME_WINDOWS_MR = "windowsMR"; static const QString TAB_KEYBOARD_MOUSE = "kbm"; static const QString TAB_GAMEPAD = "gamepad"; @@ -3237,9 +3236,13 @@ void Application::showHelp() { } else if (PluginUtils::isOculusTouchControllerAvailable()) { defaultTab = TAB_HAND_CONTROLLERS; handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH; + } else if (qApp->getActiveDisplayPlugin()->getName() == "WindowMS") { + defaultTab = TAB_HAND_CONTROLLERS; + handControllerName = HAND_CONTROLLER_NAME_WINDOWS_MR; } else if (PluginUtils::isXboxControllerAvailable()) { defaultTab = TAB_GAMEPAD; } + // TODO need some way to detect windowsMR to load controls reference default tab in Help > Controls Reference menu. QUrlQuery queryString; queryString.addQueryItem("handControllerName", handControllerName); @@ -3265,13 +3268,22 @@ void Application::resizeGL() { // Set the desired FBO texture size. If it hasn't changed, this does nothing. // Otherwise, it must rebuild the FBOs uvec2 framebufferSize = displayPlugin->getRecommendedRenderSize(); - float renderResolutionScale = getRenderResolutionScale(); - uvec2 renderSize = uvec2(vec2(framebufferSize) * renderResolutionScale); + uvec2 renderSize = uvec2(framebufferSize); if (_renderResolution != renderSize) { _renderResolution = renderSize; DependencyManager::get()->setFrameBufferSize(fromGlm(renderSize)); } + auto renderResolutionScale = getRenderResolutionScale(); + if (displayPlugin->getRenderResolutionScale() != renderResolutionScale) { + auto renderConfig = _renderEngine->getConfiguration(); + assert(renderConfig); + auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask"); + assert(mainView); + mainView->setProperty("resolutionScale", renderResolutionScale); + displayPlugin->setRenderResolutionScale(renderResolutionScale); + } + // FIXME the aspect ratio for stereo displays is incorrect based on this. float aspectRatio = displayPlugin->getRecommendedAspectRatio(); _myCamera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), aspectRatio, @@ -3283,7 +3295,6 @@ void Application::resizeGL() { } DependencyManager::get()->resize(fromGlm(displayPlugin->getRecommendedUiSize())); - displayPlugin->setRenderResolutionScale(renderResolutionScale); } void Application::handleSandboxStatus(QNetworkReply* reply) { @@ -8332,6 +8343,13 @@ void Application::copyToClipboard(const QString& text) { } #if defined(Q_OS_ANDROID) +void Application::beforeEnterBackground() { + auto nodeList = DependencyManager::get(); + nodeList->setSendDomainServerCheckInEnabled(false); + nodeList->reset(true); + clearDomainOctreeDetails(); +} + void Application::enterBackground() { QMetaObject::invokeMethod(DependencyManager::get().data(), "stop", Qt::BlockingQueuedConnection); @@ -8346,6 +8364,8 @@ void Application::enterForeground() { if (!getActiveDisplayPlugin() || getActiveDisplayPlugin()->isActive() || !getActiveDisplayPlugin()->activate()) { qWarning() << "Could not re-activate display plugin"; } + auto nodeList = DependencyManager::get(); + nodeList->setSendDomainServerCheckInEnabled(true); } #endif diff --git a/interface/src/Application.h b/interface/src/Application.h index e866649079..94e561e550 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -313,6 +313,7 @@ public: Q_INVOKABLE void copyToClipboard(const QString& text); #if defined(Q_OS_ANDROID) + void beforeEnterBackground(); void enterBackground(); void enterForeground(); #endif diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 6b4840e3e5..6648fa2eb7 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -102,7 +102,7 @@ void Application::paintGL() { PerformanceTimer perfTimer("renderOverlay"); // NOTE: There is no batch associated with this renderArgs // the ApplicationOverlay class assumes it's viewport is setup to be the device size - renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize() * getRenderResolutionScale()); + renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize()); _applicationOverlay.renderOverlay(&renderArgs); } @@ -118,7 +118,7 @@ void Application::paintGL() { // Primary rendering pass auto framebufferCache = DependencyManager::get(); finalFramebufferSize = framebufferCache->getFrameBufferSize(); - // Final framebuffer that will be handled to the display-plugin + // Final framebuffer that will be handed to the display-plugin finalFramebuffer = framebufferCache->getFramebuffer(); } diff --git a/interface/src/CrashHandler.h b/interface/src/CrashHandler.h index 4a6483c700..6f8e9c3bf6 100644 --- a/interface/src/CrashHandler.h +++ b/interface/src/CrashHandler.h @@ -14,8 +14,7 @@ #include -bool startCrashHandler(); +bool startCrashHandler(std::string appPath); void setCrashAnnotation(std::string name, std::string value); - -#endif \ No newline at end of file +#endif // hifi_CrashHandler_h diff --git a/interface/src/CrashHandler_Breakpad.cpp b/interface/src/CrashHandler_Breakpad.cpp index f2a174b6ea..c21bfa95e0 100644 --- a/interface/src/CrashHandler_Breakpad.cpp +++ b/interface/src/CrashHandler_Breakpad.cpp @@ -9,10 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "CrashHandler.h" - #if HAS_BREAKPAD +#include "CrashHandler.h" + #include #include @@ -23,8 +23,10 @@ #include #include -#include #include +#include +#include +#include google_breakpad::ExceptionHandler* gBreakpadHandler; @@ -55,11 +57,14 @@ void flushAnnotations() { settings.sync(); } -bool startCrashHandler() { +bool startCrashHandler(std::string appPath) { annotations["version"] = BuildInfo::VERSION; annotations["build_number"] = BuildInfo::BUILD_NUMBER; annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING; + auto machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); + annotations["machine_fingerprint"] = machineFingerPrint; + flushAnnotations(); gBreakpadHandler = new google_breakpad::ExceptionHandler( diff --git a/interface/src/CrashHandler_Crashpad.cpp b/interface/src/CrashHandler_Crashpad.cpp index 76d4a8e2e1..d1b5103990 100644 --- a/interface/src/CrashHandler_Crashpad.cpp +++ b/interface/src/CrashHandler_Crashpad.cpp @@ -9,21 +9,24 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#if HAS_CRASHPAD + #include "CrashHandler.h" #include -#include - -#if HAS_CRASHPAD - #include #include -#include +#include #include +#include -#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++14-extensions" +#endif #include #include @@ -31,19 +34,32 @@ #include #include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + #include +#include +#include using namespace crashpad; static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL }; static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN }; -extern QString qAppFileName(); - CrashpadClient* client { nullptr }; std::mutex annotationMutex; crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr }; +#if defined(Q_OS_WIN) +static const QString CRASHPAD_HANDLER_NAME { "crashpad_handler.exe" }; +#else +static const QString CRASHPAD_HANDLER_NAME { "crashpad_handler" }; +#endif + +#ifdef Q_OS_WIN +#include + LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { if (!client) { return EXCEPTION_CONTINUE_SEARCH; @@ -56,8 +72,9 @@ LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { return EXCEPTION_CONTINUE_SEARCH; } +#endif -bool startCrashHandler() { +bool startCrashHandler(std::string appPath) { if (BACKTRACE_URL.empty() || BACKTRACE_TOKEN.empty()) { return false; } @@ -73,6 +90,10 @@ bool startCrashHandler() { annotations["build_number"] = BuildInfo::BUILD_NUMBER.toStdString(); annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING.toStdString(); + auto machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); + annotations["machine_fingerprint"] = machineFingerPrint.toStdString(); + + arguments.push_back("--no-rate-limit"); // Setup Crashpad DB directory @@ -82,7 +103,10 @@ bool startCrashHandler() { const auto crashpadDbPath = crashpadDbDir.toStdString() + "/" + crashpadDbName; // Locate Crashpad handler - const std::string CRASHPAD_HANDLER_PATH = QFileInfo(qAppFileName()).absolutePath().toStdString() + "/crashpad_handler.exe"; + const QFileInfo interfaceBinary { QString::fromStdString(appPath) }; + const QDir interfaceDir = interfaceBinary.dir(); + assert(interfaceDir.exists(CRASHPAD_HANDLER_NAME)); + const std::string CRASHPAD_HANDLER_PATH = interfaceDir.filePath(CRASHPAD_HANDLER_NAME).toStdString(); // Setup different file paths base::FilePath::StringType dbPath; @@ -101,20 +125,24 @@ bool startCrashHandler() { // Enable automated uploads. database->GetSettings()->SetUploadsEnabled(true); +#ifdef Q_OS_WIN AddVectoredExceptionHandler(0, vectoredExceptionHandler); +#endif return client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true); } void setCrashAnnotation(std::string name, std::string value) { - std::lock_guard guard(annotationMutex); - if (!crashpadAnnotations) { - crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak - crashpad::CrashpadInfo* crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo(); - crashpad_info->set_simple_annotations(crashpadAnnotations); + if (client) { + std::lock_guard guard(annotationMutex); + if (!crashpadAnnotations) { + crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak + crashpad::CrashpadInfo* crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo(); + crashpad_info->set_simple_annotations(crashpadAnnotations); + } + std::replace(value.begin(), value.end(), ',', ';'); + crashpadAnnotations->SetKeyValue(name, value); } - std::replace(value.begin(), value.end(), ',', ';'); - crashpadAnnotations->SetKeyValue(name, value); } #endif diff --git a/interface/src/CrashHandler_None.cpp b/interface/src/CrashHandler_None.cpp index cba585f7b7..77b8ab332e 100644 --- a/interface/src/CrashHandler_None.cpp +++ b/interface/src/CrashHandler_None.cpp @@ -9,14 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#if !defined(HAS_CRASHPAD) && !defined(HAS_BREAKPAD) + #include "CrashHandler.h" #include + #include -#if !defined(HAS_CRASHPAD) && !defined(HAS_BREAKPAD) - -bool startCrashHandler() { +bool startCrashHandler(std::string appPath) { qDebug() << "No crash handler available."; return false; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 29ad71ead6..bb9a78d546 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "AvatarLogging.h" @@ -668,3 +669,49 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV DependencyManager::get()->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer); } } + + QVariantMap AvatarManager::getPalData(const QList specificAvatarIdentifiers) { + QJsonArray palData; + + auto avatarMap = getHashCopy(); + AvatarHash::iterator itr = avatarMap.begin(); + while (itr != avatarMap.end()) { + std::shared_ptr avatar = std::static_pointer_cast(*itr); + QString currentSessionUUID = avatar->getSessionUUID().toString(); + if (specificAvatarIdentifiers.isEmpty() || specificAvatarIdentifiers.contains(currentSessionUUID)) { + QJsonObject thisAvatarPalData; + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + + if (currentSessionUUID == myAvatar->getSessionUUID().toString()) { + currentSessionUUID = ""; + } + + thisAvatarPalData.insert("sessionUUID", currentSessionUUID); + thisAvatarPalData.insert("sessionDisplayName", avatar->getSessionDisplayName()); + thisAvatarPalData.insert("audioLoudness", avatar->getAudioLoudness()); + thisAvatarPalData.insert("isReplicated", avatar->getIsReplicated()); + + glm::vec3 position = avatar->getWorldPosition(); + QJsonObject jsonPosition; + jsonPosition.insert("x", position.x); + jsonPosition.insert("y", position.y); + jsonPosition.insert("z", position.z); + thisAvatarPalData.insert("position", jsonPosition); + + float palOrbOffset = 0.2f; + int headIndex = avatar->getJointIndex("Head"); + if (headIndex > 0) { + glm::vec3 jointTranslation = avatar->getAbsoluteJointTranslationInObjectFrame(headIndex); + palOrbOffset = jointTranslation.y / 2; + } + thisAvatarPalData.insert("palOrbOffset", palOrbOffset); + + palData.append(thisAvatarPalData); + } + ++itr; + } + QJsonObject doc; + doc.insert("data", palData); + return doc.toVariantMap(); +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ff29c1b381..53461146e5 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -157,6 +157,17 @@ public: */ Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value); + /**jsdoc + * Used in the PAL for getting PAL-related data about avatars nearby. Using this method is faster + * than iterating over each avatar and obtaining data about them in JavaScript, as that method + * locks and unlocks each avatar's data structure potentially hundreds of times per update tick. + * @function AvatarManager.getPalData + * @param {string[]} specificAvatarIdentifiers - A list of specific Avatar Identifiers about + * which you want to get PAL data + * @returns {object} + */ + Q_INVOKABLE QVariantMap getPalData(const QList specificAvatarIdentifiers = QList()); + float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index dbb1d8a56c..d084ca69f9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3551,6 +3551,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } if (myAvatar.getCenterOfGravityModelEnabled()) { if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { @@ -3568,6 +3569,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } else { if (!isActive(Rotation) && getForceActivateRotation()) { activate(Rotation); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); setForceActivateRotation(false); } if (!isActive(Horizontal) && getForceActivateHorizontal()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e795d9356d..819d5b0066 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -883,6 +883,7 @@ public: virtual void rebuildCollisionShape() override; const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; } + void setHeadControllerFacingMovingAverage(glm::vec2 currentHeadControllerFacing) { _headControllerFacingMovingAverage = currentHeadControllerFacing; } float getCurrentStandingHeight() const { return _currentStandingHeight; } void setCurrentStandingHeight(float newMode) { _currentStandingHeight = newMode; } const glm::quat getAverageHeadRotation() const { return _averageHeadRotation; } diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index b960c0b703..1f44343bdc 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -208,7 +208,7 @@ void QmlCommerce::alreadyOwned(const QString& marketplaceId) { ledger->alreadyOwned(marketplaceId); } -QString QmlCommerce::getInstalledApps() { +QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) { QString installedAppsFromMarketplace; QStringList runningScripts = DependencyManager::get()->getRunningScripts(); @@ -217,6 +217,18 @@ QString QmlCommerce::getInstalledApps() { foreach(QString appFileName, apps) { installedAppsFromMarketplace += appFileName; installedAppsFromMarketplace += ","; + + // If we were supplied a "justInstalledAppID" argument, that means we're entering this function + // to get the new list of installed apps immediately after installing an app. + // In that case, the app we installed may not yet have its associated script running - + // that task is asynchronous and takes a nonzero amount of time. This is especially true + // for apps that are not in Interface's script cache. + // Thus, we protect against deleting the .app.json from the user's disk (below) + // by skipping that check for the app we just installed. + if ((justInstalledAppID != "") && ((justInstalledAppID + ".app.json") == appFileName)) { + continue; + } + QFile appFile(_appsPath + appFileName); if (appFile.open(QIODevice::ReadOnly)) { QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll()); @@ -291,7 +303,8 @@ bool QmlCommerce::installApp(const QString& itemHref) { return false; } - emit appInstalled(itemHref); + QFileInfo appFileInfo(appFile); + emit appInstalled(appFileInfo.baseName()); return true; }); request->send(); @@ -321,7 +334,8 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) { qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" << appHref.fileName(); } - emit appUninstalled(itemHref); + QFileInfo appFileInfo(appFile); + emit appUninstalled(appFileInfo.baseName()); return true; } diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index a0c6916799..79d8e82e71 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -53,8 +53,8 @@ signals: void contentSetChanged(const QString& contentSetHref); - void appInstalled(const QString& appHref); - void appUninstalled(const QString& appHref); + void appInstalled(const QString& appID); + void appUninstalled(const QString& appID); protected: Q_INVOKABLE void getWalletStatus(); @@ -86,7 +86,7 @@ protected: Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID); - Q_INVOKABLE QString getInstalledApps(); + Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = ""); Q_INVOKABLE bool installApp(const QString& appHref); Q_INVOKABLE bool uninstallApp(const QString& appHref); Q_INVOKABLE bool openApp(const QString& appHref); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index d6665f1036..85a83d88d1 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -91,7 +91,7 @@ int main(int argc, const char* argv[]) { qDebug() << "UserActivityLogger is enabled:" << ual.isEnabled(); if (ual.isEnabled()) { - auto crashHandlerStarted = startCrashHandler(); + auto crashHandlerStarted = startCrashHandler(argv[0]); qDebug() << "Crash handler started:" << crashHandlerStarted; } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index afbf7f4035..ba86925581 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -86,10 +86,6 @@ void WindowScriptingInterface::raise() { }); } -void WindowScriptingInterface::raiseMainWindow() { - raise(); -} - /// Display an alert box /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index dc868e6fcd..77895e0e76 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -80,13 +80,6 @@ public slots: */ void raise(); - /**jsdoc - * Raise the Interface window if it is minimized. If raised, the window gains focus. - * @function Window.raiseMainWindow - * @deprecated Use {@link Window.raise|raise} instead. - */ - void raiseMainWindow(); - /**jsdoc * Display a dialog with the specified message and an "OK" button. The dialog is non-modal; the script continues without * waiting for a user response. diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ea660fb0e2..108f20b2dd 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -179,7 +179,7 @@ static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPT void ApplicationOverlay::buildFramebufferObject() { PROFILE_RANGE(app, __FUNCTION__); - auto uiSize = glm::uvec2(glm::vec2(qApp->getUiSize()) * qApp->getRenderResolutionScale()); + auto uiSize = glm::uvec2(qApp->getUiSize()); if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) { _overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ApplicationOverlay")); } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index c7ee868855..6bb615948c 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -333,7 +333,13 @@ void Stats::updateStats(bool force) { } auto gpuContext = qApp->getGPUContext(); - + auto displayPlugin = qApp->getActiveDisplayPlugin(); + if (displayPlugin) { + QVector2D dims(displayPlugin->getRecommendedRenderSize().x, displayPlugin->getRecommendedRenderSize().y); + dims *= displayPlugin->getRenderResolutionScale(); + STAT_UPDATE(gpuFrameSize, dims); + STAT_UPDATE(gpuFrameTimePerPixel, (float)(gpuContext->getFrameTimerGPUAverage()*1000000.0 / double(dims.x()*dims.y()))); + } // Update Frame timing (in ms) STAT_UPDATE(gpuFrameTime, (float)gpuContext->getFrameTimerGPUAverage()); STAT_UPDATE(batchFrameTime, (float)gpuContext->getFrameTimerBatchAverage()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 36e923261d..f4181f9788 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -276,7 +276,9 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, gpuTextureExternalMemory, 0) STATS_PROPERTY(QString, gpuTextureMemoryPressureState, QString()) STATS_PROPERTY(int, gpuFreeMemory, 0) + STATS_PROPERTY(QVector2D, gpuFrameSize, QVector2D(0,0)) STATS_PROPERTY(float, gpuFrameTime, 0) + STATS_PROPERTY(float, gpuFrameTimePerPixel, 0) STATS_PROPERTY(float, batchFrameTime, 0) STATS_PROPERTY(float, engineFrameTime, 0) STATS_PROPERTY(float, avatarSimulationTime, 0) @@ -962,6 +964,20 @@ signals: */ void gpuFrameTimeChanged(); + /**jsdoc + * Triggered when the value of the gpuFrameTime property changes. + * @function Stats.gpuFrameTimeChanged + * @returns {Signal} + */ + void gpuFrameSizeChanged(); + + /**jsdoc + * Triggered when the value of the gpuFrameTime property changes. + * @function Stats.gpuFrameTimeChanged + * @returns {Signal} + */ + void gpuFrameTimePerPixelChanged(); + /**jsdoc * Triggered when the value of the batchFrameTime property changes. * @function Stats.batchFrameTimeChanged diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index aca186a589..c6323614c5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -97,6 +97,10 @@ static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; void ContextOverlayInterface::setEnabled(bool enabled) { _enabled = enabled; + if (!enabled) { + // Destroy any potentially-active ContextOverlays when disabling the interface + createOrDestroyContextOverlay(EntityItemID(), PointerEvent()); + } } void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 4f2a8e6fa4..5a576c6d78 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -116,7 +116,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { auto geometryCache = DependencyManager::get(); auto textureCache = DependencyManager::get(); - auto size = glm::uvec2(glm::vec2(qApp->getUiSize()) * qApp->getRenderResolutionScale()); + auto size = glm::uvec2(qApp->getUiSize()); int width = size.x; int height = size.y; mat4 legacyProjection = glm::ortho(0, width, height, 0, -1000, 1000); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index fe9a347c20..157f7b2ec6 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -381,7 +381,7 @@ public slots: /**jsdoc * Get the rotation of the left palm in world coordinates. * @function MyAvatar.getLeftPalmRotation - * @returns {Vec3} The rotation of the left palm in world coordinates. + * @returns {Quat} The rotation of the left palm in world coordinates. * @example Report the rotation of your avatar's left palm. * print(JSON.stringify(MyAvatar.getLeftPalmRotation())); */ @@ -398,7 +398,7 @@ public slots: /**jsdoc * Get the rotation of the right palm in world coordinates. * @function MyAvatar.getRightPalmRotation - * @returns {Vec3} The rotation of the right palm in world coordinates. + * @returns {Quat} The rotation of the right palm in world coordinates. * @example Report the rotation of your avatar's right palm. * print(JSON.stringify(MyAvatar.getRightPalmRotation())); */ diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index b78f00fa0e..efa4859b42 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -275,7 +275,7 @@ bool CompositorHelper::getReticleOverDesktop() const { // as being over the desktop. if (isHMD()) { QMutexLocker locker(&_reticleLock); - glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()) * _currentDisplayPlugin->getRenderResolutionScale(); + glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()); static const glm::vec2 minOverlayPosition; if (glm::any(glm::lessThan(_reticlePositionInHMD, minOverlayPosition)) || glm::any(glm::greaterThan(_reticlePositionInHMD, maxOverlayPosition))) { @@ -317,7 +317,7 @@ void CompositorHelper::sendFakeMouseEvent() { void CompositorHelper::setReticlePosition(const glm::vec2& position, bool sendFakeEvent) { if (isHMD()) { - glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()) * _currentDisplayPlugin->getRenderResolutionScale(); + glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()); // FIXME don't allow negative mouseExtra glm::vec2 mouseExtra = (MOUSE_EXTENTS_PIXELS - maxOverlayPosition) / 2.0f; glm::vec2 minMouse = vec2(0) - mouseExtra; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 0d556544bb..9200843cf8 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -888,7 +888,7 @@ OpenGLDisplayPlugin::~OpenGLDisplayPlugin() { } void OpenGLDisplayPlugin::updateCompositeFramebuffer() { - auto renderSize = glm::uvec2(glm::vec2(getRecommendedRenderSize()) * getRenderResolutionScale()); + auto renderSize = glm::uvec2(getRecommendedRenderSize()); if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) { _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index e8f57ea834..78801df715 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -363,12 +363,18 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity return false; } -void EntityRenderer::updateModelTransform() { +void EntityRenderer::updateModelTransformAndBound() { bool success = false; auto newModelTransform = _entity->getTransformToCenter(success); if (success) { _modelTransform = newModelTransform; } + + success = false; + auto bound = _entity->getAABox(success); + if (success) { + _bound = bound; + } } void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { @@ -380,15 +386,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa } _prevIsTransparent = transparent; - bool success = false; - auto bound = entity->getAABox(success); - if (success) { - _bound = bound; - } - auto newModelTransform = entity->getTransformToCenter(success); - if (success) { - _modelTransform = newModelTransform; - } + updateModelTransformAndBound(); _moving = entity->isMovingRelativeToParent(); _visible = entity->getVisible(); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 40966c4f41..496649eb5f 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -97,7 +97,7 @@ protected: virtual void doRender(RenderArgs* args) = 0; bool isFading() const { return _isFading; } - void updateModelTransform(); + void updateModelTransformAndBound(); virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; } inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index a6a6dc05f2..309671f49e 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -126,7 +126,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi void* key = (void*)this; AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () { withWriteLock([&] { - updateModelTransform(); + updateModelTransformAndBound(); _renderTransform = getModelTransform(); }); }); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 2e0656ab81..c50b3bd760 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -106,10 +106,8 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce _position = entity->getWorldPosition(); _dimensions = entity->getScaledDimensions(); _orientation = entity->getWorldOrientation(); - bool success = false; - auto newModelTransform = entity->getTransformToCenter(success); - _renderTransform = success ? newModelTransform : getModelTransform(); - + updateModelTransformAndBound(); + _renderTransform = getModelTransform(); if (_shape == entity::Sphere) { _renderTransform.postScale(SPHERE_ENTITY_SCALE); } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index ce4b6d9175..08a3b585e4 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -70,7 +70,7 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () { withWriteLock([&] { _dimensions = entity->getScaledDimensions(); - updateModelTransform(); + updateModelTransformAndBound(); _renderTransform = getModelTransform(); }); }); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 17d6d58781..793b4aa158 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -206,7 +206,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene glm::vec2 windowSize = getWindowSize(entity); _webSurface->resize(QSize(windowSize.x, windowSize.y)); - updateModelTransform(); + updateModelTransformAndBound(); _renderTransform = getModelTransform(); _renderTransform.postScale(entity->getScaledDimensions()); }); diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 2db85eb7ac..95bdae43b9 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -22,13 +22,14 @@ const float AnimationPropertyGroup::MAXIMUM_POSSIBLE_FRAME = 100000.0f; bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { return - (a._currentFrame == b._currentFrame) && (a._running == b._running) && (a._loop == b._loop) && (a._hold == b._hold) && (a._firstFrame == b._firstFrame) && (a._lastFrame == b._lastFrame) && + (a._fps == b._fps) && + (a._allowTranslation == b._allowTranslation) && (a._url == b._url); } @@ -40,6 +41,8 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b (a._hold != b._hold) || (a._firstFrame != b._firstFrame) || (a._lastFrame != b._lastFrame) || + (a._fps != b._fps) || + (a._allowTranslation != b._allowTranslation) || (a._url != b._url); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 31ec189cf9..8e382fabd4 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2414,11 +2414,7 @@ bool EntityItem::shouldSuppressLocationEdits() const { } // if any of the ancestors are MyAvatar, suppress - if (isChildOfMyAvatar()) { - return true; - } - - return false; + return isChildOfMyAvatar(); } QList EntityItem::getActionsOfType(EntityDynamicType typeToGet) const { diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 1fa4b3873e..300e6f4846 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -21,14 +21,17 @@ #include #include + #include #include #include +#include #include #include #include +#include #include "FBXReader.h" @@ -786,13 +789,18 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { QVector raw_vertices; QVector raw_normals; - addArrayOfType(indicesBuffer.blob, + bool success = addArrayOfType(indicesBuffer.blob, indicesBufferview.byteOffset + indicesAccBoffset, - indicesBufferview.byteLength, + indicesAccessor.count, part.triangleIndices, indicesAccessor.type, indicesAccessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; + continue; + } + QList keys = primitive.attributes.values.keys(); foreach(auto &key, keys) { @@ -805,44 +813,60 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0; if (key == "POSITION") { QVector vertices; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, vertices, + accessor.count, vertices, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url; + continue; + } for (int n = 0; n < vertices.size(); n = n + 3) { mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2])); } } else if (key == "NORMAL") { QVector normals; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, + accessor.count, normals, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url; + continue; + } for (int n = 0; n < normals.size(); n = n + 3) { mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2])); } } else if (key == "TEXCOORD_0") { QVector texcoords; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, + accessor.count, texcoords, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; + continue; + } for (int n = 0; n < texcoords.size(); n = n + 2) { mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); } } else if (key == "TEXCOORD_1") { QVector texcoords; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, + accessor.count, texcoords, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; + continue; + } for (int n = 0; n < texcoords.size(); n = n + 2) { mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); } @@ -888,8 +912,16 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) { + _url = url; + // Normalize url for local files + QUrl normalizeUrl = DependencyManager::get()->normalizeURL(url); + if (normalizeUrl.scheme().isEmpty() || (normalizeUrl.scheme() == "file")) { + QString localFileName = PathUtils::expandToLocalDataAbsolutePath(normalizeUrl).toLocalFile(); + _url = QUrl(QFileInfo(localFileName).absoluteFilePath()); + } + parseGLTF(model); //_file.dump(); FBXGeometry* geometryPtr = new FBXGeometry(); @@ -904,6 +936,7 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping bool GLTFReader::readBinary(const QString& url, QByteArray& outdata) { QUrl binaryUrl = _url.resolved(QUrl(url).fileName()); + qCDebug(modelformat) << "binaryUrl: " << binaryUrl << " OriginalUrl: " << _url; bool success; std::tie(success, outdata) = requestData(binaryUrl); @@ -1018,13 +1051,12 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia fbxmat.opacityTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); fbxmat.albedoTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); fbxmat.useAlbedoMap = true; - fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); - fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { fbxmat.roughnessTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useRoughnessMap = true; - + fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { fbxmat._material->setRoughness(material.pbrMetallicRoughness.roughnessFactor); @@ -1043,7 +1075,7 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia } template -bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength, +bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType) { QDataStream blobstream(bin); @@ -1051,142 +1083,77 @@ bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength blobstream.setVersion(QDataStream::Qt_5_9); blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision); - int vsize = byteLength / sizeof(T); - - qCDebug(modelformat) << "size1: " << vsize; + qCDebug(modelformat) << "size1: " << count; int dataskipped = blobstream.skipRawData(byteOffset); qCDebug(modelformat) << "dataskipped: " << dataskipped; - - while (outarray.size() < vsize) { - - T value1, value2, value3, value4, - value5, value6, value7, value8, - value9, value10, value11, value12, - value13, value14, value15, value16; - - if (accessorType == GLTFAccessorType::SCALAR) { - - blobstream >> value1; - - outarray.push_back(value1); - } else if (accessorType == GLTFAccessorType::VEC2) { - - blobstream >> value1; - blobstream >> value2; - - outarray.push_back(value1); - outarray.push_back(value2); - } else if (accessorType == GLTFAccessorType::VEC3) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - } else if (accessorType == GLTFAccessorType::VEC4 || accessorType == GLTFAccessorType::MAT2) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - blobstream >> value4; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - outarray.push_back(value4); - } else if (accessorType == GLTFAccessorType::MAT3) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - blobstream >> value4; - blobstream >> value5; - blobstream >> value6; - blobstream >> value7; - blobstream >> value8; - blobstream >> value9; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - outarray.push_back(value4); - outarray.push_back(value5); - outarray.push_back(value6); - outarray.push_back(value7); - outarray.push_back(value8); - outarray.push_back(value9); - } else if (accessorType == GLTFAccessorType::MAT4) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - blobstream >> value4; - blobstream >> value5; - blobstream >> value6; - blobstream >> value7; - blobstream >> value8; - blobstream >> value9; - blobstream >> value10; - blobstream >> value11; - blobstream >> value12; - blobstream >> value13; - blobstream >> value14; - blobstream >> value15; - blobstream >> value16; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - outarray.push_back(value4); - outarray.push_back(value5); - outarray.push_back(value6); - outarray.push_back(value7); - outarray.push_back(value8); - outarray.push_back(value9); - outarray.push_back(value10); - outarray.push_back(value11); - outarray.push_back(value12); - outarray.push_back(value13); - outarray.push_back(value14); - outarray.push_back(value15); - outarray.push_back(value16); - + int bufferCount = 0; + switch (accessorType) { + case GLTFAccessorType::SCALAR: + bufferCount = 1; + break; + case GLTFAccessorType::VEC2: + bufferCount = 2; + break; + case GLTFAccessorType::VEC3: + bufferCount = 3; + break; + case GLTFAccessorType::VEC4: + bufferCount = 4; + break; + case GLTFAccessorType::MAT2: + bufferCount = 4; + break; + case GLTFAccessorType::MAT3: + bufferCount = 9; + break; + case GLTFAccessorType::MAT4: + bufferCount = 16; + break; + default: + qWarning(modelformat) << "Unknown accessorType: " << accessorType; + blobstream.unsetDevice(); + return false; + } + for (int i = 0; i < count; i++) { + for (int j = 0; j < bufferCount; j++) { + if (!blobstream.atEnd()) { + T value; + blobstream >> value; + outarray.push_back(value); + } else { + blobstream.unsetDevice(); + return false; + } } } + blobstream.unsetDevice(); return true; } template -bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength, +bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType, int componentType) { switch (componentType) { case GLTFAccessorComponentType::BYTE: {} case GLTFAccessorComponentType::UNSIGNED_BYTE: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::SHORT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::UNSIGNED_INT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::UNSIGNED_SHORT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::FLOAT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } } - return true; + return false; } void GLTFReader::retriangulate(const QVector& inIndices, const QVector& in_vertices, diff --git a/libraries/fbx/src/GLTFReader.h b/libraries/fbx/src/GLTFReader.h index 28c1d8282f..2183256b87 100644 --- a/libraries/fbx/src/GLTFReader.h +++ b/libraries/fbx/src/GLTFReader.h @@ -762,11 +762,11 @@ private: bool readBinary(const QString& url, QByteArray& outdata); template - bool readArray(const QByteArray& bin, int byteOffset, int byteLength, + bool readArray(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType); template - bool addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength, + bool addArrayOfType(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType, int componentType); void retriangulate(const QVector& in_indices, const QVector& in_vertices, diff --git a/libraries/graphics/src/graphics/Haze.cpp b/libraries/graphics/src/graphics/Haze.cpp index d5a060b90b..ded48429ba 100644 --- a/libraries/graphics/src/graphics/Haze.cpp +++ b/libraries/graphics/src/graphics/Haze.cpp @@ -177,9 +177,9 @@ void Haze::setHazeBaseReference(const float hazeBaseReference) { void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) { auto& params = _hazeParametersBuffer.get(); - - if (params.hazeBackgroundBlend != hazeBackgroundBlend) { - _hazeParametersBuffer.edit().hazeBackgroundBlend = hazeBackgroundBlend; + auto newBlend = 1.0f - hazeBackgroundBlend; + if (params.hazeBackgroundBlend != newBlend) { + _hazeParametersBuffer.edit().hazeBackgroundBlend = newBlend; } } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index d9d5c21e5d..2ce734dd26 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -289,6 +289,12 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) } void NodeList::sendDomainServerCheckIn() { + + if (!_sendDomainServerCheckInEnabled) { + qCDebug(networking) << "Refusing to send a domain-server check in while it is disabled."; + return; + } + if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "sendDomainServerCheckIn", Qt::QueuedConnection); return; diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index c5cf5e9524..78d3fad696 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -90,6 +90,9 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool isRequesting); + bool getSendDomainServerCheckInEnabled() { return _sendDomainServerCheckInEnabled; } + void setSendDomainServerCheckInEnabled(bool enabled) { _sendDomainServerCheckInEnabled = enabled; } + void removeFromIgnoreMuteSets(const QUuid& nodeID); virtual bool isDomainServer() const override { return false; } @@ -169,6 +172,8 @@ private: QTimer _keepAlivePingTimer; bool _requestsDomainListData { false }; + bool _sendDomainServerCheckInEnabled { true }; + mutable QReadWriteLock _ignoredSetLock; tbb::concurrent_unordered_set _ignoredNodeIDs; mutable QReadWriteLock _personalMutedSetLock; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index aaf2c0a46b..cee0e6a1fa 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -262,24 +262,53 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling. btVector3 endPos = startPos + linearDisplacement; + // resolve the simple linearDisplacement + _followLinearDisplacement += linearDisplacement; + + // now for the rotational part... btQuaternion startRot = bodyTransform.getRotation(); btQuaternion desiredRot = _followDesiredBodyTransform.getRotation(); - if (desiredRot.dot(startRot) < 0.0f) { - desiredRot = -desiredRot; + + // startRot as default rotation + btQuaternion endRot = startRot; + + // the dot product between two quaternions is equal to +/- cos(angle/2) + // where 'angle' is that of the rotation between them + float qDot = desiredRot.dot(startRot); + + // when the abs() value of the dot product is approximately 1.0 + // then the two rotations are effectively adjacent + const float MIN_DOT_PRODUCT_OF_ADJACENT_QUATERNIONS = 0.99999f; // corresponds to approx 0.5 degrees + if (fabsf(qDot) < MIN_DOT_PRODUCT_OF_ADJACENT_QUATERNIONS) { + if (qDot < 0.0f) { + // the quaternions are actually on opposite hyperhemispheres + // so we move one to agree with the other and negate qDot + desiredRot = -desiredRot; + qDot = -qDot; + } + btQuaternion deltaRot = desiredRot * startRot.inverse(); + + // the axis is the imaginary part, but scaled by sin(angle/2) + btVector3 axis(deltaRot.getX(), deltaRot.getY(), deltaRot.getZ()); + axis /= sqrtf(1.0f - qDot * qDot); + + // compute the angle we will resolve for this dt, but don't overshoot + float angle = 2.0f * acosf(qDot); + if ( dt < _followTimeRemaining) { + angle *= dt / _followTimeRemaining; + } + + // accumulate rotation + deltaRot = btQuaternion(axis, angle); + _followAngularDisplacement = (deltaRot * _followAngularDisplacement).normalize(); + + // in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. + btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset); + + endRot = deltaRot * startRot; + btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset); + _followLinearDisplacement += swingDisplacement; } - btQuaternion deltaRot = desiredRot * startRot.inverse(); - float angularSpeed = deltaRot.getAngle() / _followTimeRemaining; - glm::vec3 rotationAxis = glm::normalize(glm::axis(bulletToGLM(deltaRot))); // deltaRot.getAxis() is inaccurate - btQuaternion angularDisplacement = btQuaternion(glmToBullet(rotationAxis), angularSpeed * dt); - btQuaternion endRot = angularDisplacement * startRot; - - // in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. - btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset); - btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset); - - _followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement; - _followAngularDisplacement = angularDisplacement * _followAngularDisplacement; - _rigidBody->setWorldTransform(btTransform(endRot, endPos)); } _followTime += dt; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9089f02aaf..a610a6f2a6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -234,7 +234,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { + if (_motionType == MOTION_TYPE_KINEMATIC) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. @@ -327,13 +327,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } - bool parentTransformSuccess; - Transform localToWorld = _entity->getParentTransform(parentTransformSuccess); - Transform worldToLocal; - if (parentTransformSuccess) { - localToWorld.evalInverse(worldToLocal); - } - int numSteps = simulationStep - _lastStep; float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP; @@ -361,6 +354,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } + if (_body->isStaticOrKinematicObject()) { + return false; + } + _lastStep = simulationStep; if (glm::length2(_serverVelocity) > 0.0f) { // the entity-server doesn't know where avatars are, so it doesn't do simple extrapolation for children of @@ -388,6 +385,12 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { // TODO: compensate for _worldOffset offset here // compute position error + bool parentTransformSuccess; + Transform localToWorld = _entity->getParentTransform(parentTransformSuccess); + Transform worldToLocal; + if (parentTransformSuccess) { + localToWorld.evalInverse(worldToLocal); + } btTransform worldTrans = _body->getWorldTransform(); glm::vec3 position = worldToLocal.transform(bulletToGLM(worldTrans.getOrigin())); @@ -407,20 +410,23 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { if (glm::length2(_serverAngularVelocity) > 0.0f) { // compute rotation error - float attenuation = powf(1.0f - _body->getAngularDamping(), dt); - _serverAngularVelocity *= attenuation; + // // Bullet caps the effective rotation velocity inside its rotation integration step, therefore // we must integrate with the same algorithm and timestep in order achieve similar results. - for (int i = 0; i < numSteps; ++i) { - _serverRotation = glm::normalize(computeBulletRotationStep(_serverAngularVelocity, - PHYSICS_ENGINE_FIXED_SUBSTEP) * _serverRotation); + float attenuation = powf(1.0f - _body->getAngularDamping(), PHYSICS_ENGINE_FIXED_SUBSTEP); + _serverAngularVelocity *= attenuation; + glm::quat rotation = computeBulletRotationStep(_serverAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP); + for (int i = 1; i < numSteps; ++i) { + _serverAngularVelocity *= attenuation; + rotation = computeBulletRotationStep(_serverAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP) * rotation; } + _serverRotation = glm::normalize(rotation * _serverRotation); + const float MIN_ROTATION_DOT = 0.99999f; // This corresponds to about 0.5 degrees of rotation + glm::quat actualRotation = worldToLocal.getRotation() * bulletToGLM(worldTrans.getRotation()); + return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT); } - const float MIN_ROTATION_DOT = 0.99999f; // This corresponds to about 0.5 degrees of rotation - glm::quat actualRotation = worldToLocal.getRotation() * bulletToGLM(worldTrans.getRotation()); - - return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT); + return false; } bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index b990d3612b..5666e75aa1 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -59,7 +59,10 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { _entitiesToAddToPhysics.insert(entity); } } else if (canBeKinematic && entity->isMovingRelativeToParent()) { - _simpleKinematicEntities.insert(entity); + SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); + if (itr == _simpleKinematicEntities.end()) { + _simpleKinematicEntities.insert(entity); + } } } @@ -150,7 +153,10 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) { removeOwnershipData(motionState); _entitiesToRemoveFromPhysics.insert(entity); if (canBeKinematic && entity->isMovingRelativeToParent()) { - _simpleKinematicEntities.insert(entity); + SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); + if (itr == _simpleKinematicEntities.end()) { + _simpleKinematicEntities.insert(entity); + } } } else { _incomingChanges.insert(motionState); @@ -160,11 +166,20 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) { // 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 + SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); + if (itr != _simpleKinematicEntities.end()) { + _simpleKinematicEntities.erase(itr); + } } else if (canBeKinematic && entity->isMovingRelativeToParent()) { - _simpleKinematicEntities.insert(entity); + SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); + if (itr == _simpleKinematicEntities.end()) { + _simpleKinematicEntities.insert(entity); + } } else { - _simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic + SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); + if (itr != _simpleKinematicEntities.end()) { + _simpleKinematicEntities.erase(itr); + } } } @@ -212,7 +227,6 @@ const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhys 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); @@ -255,7 +269,10 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re // this entity should no longer be on the internal _entitiesToAddToPhysics entityItr = _entitiesToAddToPhysics.erase(entityItr); if (entity->isMovingRelativeToParent()) { - _simpleKinematicEntities.insert(entity); + SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); + if (itr == _simpleKinematicEntities.end()) { + _simpleKinematicEntities.insert(entity); + } } } else if (entity->isReadyToComputeShape()) { ShapeInfo shapeInfo; @@ -375,19 +392,21 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta } void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) { - if (!getEntityTree()->isServerlessMode()) { - motionState->initForBid(); - motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps()); - _bids.push_back(motionState); - _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry()); + if (getEntityTree()->isServerlessMode()) { + return; } + motionState->initForBid(); + motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps()); + _bids.push_back(motionState); + _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry()); } void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) { - if (!getEntityTree()->isServerlessMode()) { - motionState->initForOwned(); - _owned.push_back(motionState); + if (getEntityTree()->isServerlessMode()) { + return; } + motionState->initForOwned(); + _owned.push_back(motionState); } void PhysicalEntitySimulation::sendOwnershipBids(uint32_t numSubsteps) { @@ -426,7 +445,9 @@ void PhysicalEntitySimulation::sendOwnershipBids(uint32_t numSubsteps) { } void PhysicalEntitySimulation::sendOwnedUpdates(uint32_t numSubsteps) { - bool serverlessMode = getEntityTree()->isServerlessMode(); + if (getEntityTree()->isServerlessMode()) { + return; + } PROFILE_RANGE_EX(simulation_physics, "Update", 0x00000000, (uint64_t)_owned.size()); uint32_t i = 0; while (i < _owned.size()) { @@ -438,7 +459,7 @@ void PhysicalEntitySimulation::sendOwnedUpdates(uint32_t numSubsteps) { } _owned.remove(i); } else { - if (!serverlessMode && _owned[i]->shouldSendUpdate(numSubsteps)) { + if (_owned[i]->shouldSendUpdate(numSubsteps)) { _owned[i]->sendUpdate(_entityPacketSender, numSubsteps); } ++i; diff --git a/libraries/render-utils/src/BloomApply.shared.slh b/libraries/render-utils/src/BloomApply.shared.slh new file mode 100644 index 0000000000..61f5a983b3 --- /dev/null +++ b/libraries/render-utils/src/BloomApply.shared.slh @@ -0,0 +1,16 @@ +// glsl / C++ compatible source as interface for BloomApply +#ifdef __cplusplus +# define BA_VEC3 glm::vec3 +#else +# define BA_VEC3 vec3 +#endif + +struct Parameters +{ + BA_VEC3 _intensities; +}; + + // <@if 1@> + // Trigger Scribe include + // <@endif@> +// diff --git a/libraries/render-utils/src/BloomApply.slf b/libraries/render-utils/src/BloomApply.slf index 961438888e..28415643a0 100644 --- a/libraries/render-utils/src/BloomApply.slf +++ b/libraries/render-utils/src/BloomApply.slf @@ -9,11 +9,15 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include BloomApply.shared.slh@> uniform sampler2D blurMap0; uniform sampler2D blurMap1; uniform sampler2D blurMap2; -uniform vec3 intensity; + +layout(std140) uniform parametersBuffer { + Parameters parameters; +}; in vec2 varTexCoord0; out vec4 outFragColor; @@ -23,5 +27,5 @@ void main(void) { vec4 blur1 = texture(blurMap1, varTexCoord0); vec4 blur2 = texture(blurMap2, varTexCoord0); - outFragColor = vec4(blur0.rgb*intensity.x + blur1.rgb*intensity.y + blur2.rgb*intensity.z, 1.0f); + outFragColor = vec4(blur0.rgb*parameters._intensities.x + blur1.rgb*parameters._intensities.y + blur2.rgb*parameters._intensities.z, 1.0f); } diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index ee06e17578..0e95655370 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -21,13 +21,15 @@ #define BLOOM_BLUR_LEVEL_COUNT 3 -BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) : - _downsamplingFactor(downsamplingFactor) { +BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) { assert(downsamplingFactor > 0); + _parameters.edit()._sampleCount = downsamplingFactor; } void BloomThreshold::configure(const Config& config) { - _threshold = config.threshold; + if (_parameters.get()._threshold != config.threshold) { + _parameters.edit()._threshold = config.threshold; + } } void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { @@ -43,10 +45,11 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons auto inputBuffer = inputFrameBuffer->getRenderBuffer(0); auto bufferSize = gpu::Vec2u(inputBuffer->getDimensions()); + const auto downSamplingFactor = _parameters.get()._sampleCount; // Downsample resolution - bufferSize.x /= _downsamplingFactor; - bufferSize.y /= _downsamplingFactor; + bufferSize.x /= downSamplingFactor; + bufferSize.y /= downSamplingFactor; if (!_outputBuffer || _outputBuffer->getSize() != bufferSize) { auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputBuffer->getTexelFormat(), bufferSize.x, bufferSize.y, @@ -54,10 +57,12 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons _outputBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold")); _outputBuffer->setRenderBuffer(0, colorTexture); + + _parameters.edit()._deltaUV = { 1.0f / bufferSize.x, 1.0f / bufferSize.y }; } static const int COLOR_MAP_SLOT = 0; - static const int THRESHOLD_SLOT = 1; + static const int PARAMETERS_SLOT = 1; if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); @@ -66,7 +71,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding("colorMap", COLOR_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding("threshold", THRESHOLD_SLOT)); + slotBindings.insert(gpu::Shader::Binding("parametersBuffer", PARAMETERS_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -86,21 +91,26 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons batch.setFramebuffer(_outputBuffer); batch.setResourceTexture(COLOR_MAP_SLOT, inputBuffer); - batch._glUniform1f(THRESHOLD_SLOT, _threshold); + batch.setUniformBuffer(PARAMETERS_SLOT, _parameters); batch.draw(gpu::TRIANGLE_STRIP, 4); }); outputs = _outputBuffer; } -BloomApply::BloomApply() : _intensities{ 1.0f, 1.0f, 1.0f } { +BloomApply::BloomApply() { } void BloomApply::configure(const Config& config) { - _intensities.x = config.intensity / 3.0f; - _intensities.y = _intensities.x; - _intensities.z = _intensities.x; + const auto newIntensity = config.intensity / 3.0f; + + if (_parameters.get()._intensities.x != newIntensity) { + auto& parameters = _parameters.edit(); + parameters._intensities.x = newIntensity; + parameters._intensities.y = newIntensity; + parameters._intensities.z = newIntensity; + } } void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { @@ -111,7 +121,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In static const auto BLUR0_SLOT = 0; static const auto BLUR1_SLOT = 1; static const auto BLUR2_SLOT = 2; - static const auto INTENSITY_SLOT = 3; + static const auto PARAMETERS_SLOT = 0; if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); @@ -122,7 +132,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In slotBindings.insert(gpu::Shader::Binding("blurMap0", BLUR0_SLOT)); slotBindings.insert(gpu::Shader::Binding("blurMap1", BLUR1_SLOT)); slotBindings.insert(gpu::Shader::Binding("blurMap2", BLUR2_SLOT)); - slotBindings.insert(gpu::Shader::Binding("intensity", INTENSITY_SLOT)); + slotBindings.insert(gpu::Shader::Binding("parametersBuffer", PARAMETERS_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -151,7 +161,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0)); batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0)); batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0)); - batch._glUniform3f(INTENSITY_SLOT, _intensities.x, _intensities.y, _intensities.z); + batch.setUniformBuffer(PARAMETERS_SLOT, _parameters); batch.draw(gpu::TRIANGLE_STRIP, 4); }); } diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 2ff6bc35a7..04cb4a9474 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -61,10 +61,11 @@ public: private: +#include "BloomThreshold.shared.slh" + gpu::FramebufferPointer _outputBuffer; gpu::PipelinePointer _pipeline; - float _threshold; - unsigned int _downsamplingFactor; + gpu::StructBuffer _parameters; }; @@ -95,8 +96,10 @@ public: private: +#include "BloomApply.shared.slh" + gpu::PipelinePointer _pipeline; - glm::vec3 _intensities; + gpu::StructBuffer _parameters; }; class BloomDraw { diff --git a/libraries/render-utils/src/BloomThreshold.shared.slh b/libraries/render-utils/src/BloomThreshold.shared.slh new file mode 100644 index 0000000000..8aaf8ec311 --- /dev/null +++ b/libraries/render-utils/src/BloomThreshold.shared.slh @@ -0,0 +1,18 @@ +// glsl / C++ compatible source as interface for BloomThreshold +#ifdef __cplusplus +# define BT_VEC2 glm::vec2 +#else +# define BT_VEC2 vec2 +#endif + +struct Parameters +{ + BT_VEC2 _deltaUV; + float _threshold; + int _sampleCount; +}; + + // <@if 1@> + // Trigger Scribe include + // <@endif@> +// diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf index e4b96618df..6eb75fba6e 100644 --- a/libraries/render-utils/src/BloomThreshold.slf +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -9,37 +9,35 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include BloomThreshold.shared.slh@> uniform sampler2D colorMap; -uniform float threshold; +layout(std140) uniform parametersBuffer { + Parameters parameters; +}; in vec2 varTexCoord0; out vec4 outFragColor; -#define DOWNSAMPLING_FACTOR 4 -#define SAMPLE_COUNT (DOWNSAMPLING_FACTOR/2) - void main(void) { - vec2 deltaX = dFdx(varTexCoord0) / SAMPLE_COUNT; - vec2 deltaY = dFdy(varTexCoord0) / SAMPLE_COUNT; vec2 startUv = varTexCoord0; vec4 maskedColor = vec4(0,0,0,0); - for (int y=0 ; y 0.0) && (hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) { - vec4 colorV4 = computeHazeColor( - vec4(color, 1.0), // fragment original color + vec4 hazeColor = computeHazeColor( positionES, // fragment position in eye coordinates fragPositionWS, // fragment position in world coordinates invViewMat[3].xyz, // eye position in world coordinates lightDirection // keylight direction vector in world coordinates ); - color = colorV4.rgb; + color = mix(color.rgb, hazeColor.rgb, hazeColor.a); } return color; @@ -273,15 +272,14 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze( // Haze if ((isHazeEnabled() > 0.0) && (hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) { - vec4 colorV4 = computeHazeColor( - vec4(color, 1.0), // fragment original color + vec4 hazeColor = computeHazeColor( positionES, // fragment position in eye coordinates positionWS, // fragment position in world coordinates invViewMat[3].xyz, // eye position in world coordinates lightDirection // keylight direction vector ); - color = colorV4.rgb; + color = mix(color.rgb, hazeColor.rgb, hazeColor.a); } return color; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 452e5b5ccd..62d8dffe3a 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -393,34 +393,42 @@ graphics::MeshPointer DeferredLightingEffect::getSpotLightMesh() { return _spotLightMesh; } -void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer) { +gpu::FramebufferPointer PreparePrimaryFramebuffer::createFramebuffer(const char* name, const glm::uvec2& frameSize) { + gpu::FramebufferPointer framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(name)); + auto colorFormat = gpu::Element::COLOR_SRGBA_32; + + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); + auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + + framebuffer->setRenderBuffer(0, primaryColorTexture); + + auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format + auto primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + + framebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); + + return framebuffer; +} + +void PreparePrimaryFramebuffer::configure(const Config& config) { + _resolutionScale = config.resolutionScale; +} + +void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, Output& primaryFramebuffer) { glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); + glm::uvec2 scaledFrameSize(glm::vec2(frameSize) * _resolutionScale); // Resizing framebuffers instead of re-building them seems to cause issues with threaded // rendering - if (_primaryFramebuffer && _primaryFramebuffer->getSize() != frameSize) { - _primaryFramebuffer.reset(); + if (!_primaryFramebuffer || _primaryFramebuffer->getSize() != scaledFrameSize) { + _primaryFramebuffer = createFramebuffer("deferredPrimary", scaledFrameSize); } - if (!_primaryFramebuffer) { - _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("deferredPrimary")); - auto colorFormat = gpu::Element::COLOR_SRGBA_32; - - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); - - - _primaryFramebuffer->setRenderBuffer(0, primaryColorTexture); - - - auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); - - _primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); - } - - primaryFramebuffer = _primaryFramebuffer; + + // Set viewport for the rest of the scaled passes + renderContext->args->_viewport.z = scaledFrameSize.x; + renderContext->args->_viewport.w = scaledFrameSize.y; } void PrepareDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 9b55083ad7..5da2eb22f7 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -93,13 +93,34 @@ private: friend class RenderDeferredCleanup; }; +class PreparePrimaryFramebufferConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float resolutionScale MEMBER resolutionScale NOTIFY dirty) +public: + + float resolutionScale{ 1.0f }; + +signals: + void dirty(); +}; + class PreparePrimaryFramebuffer { public: - using JobModel = render::Job::ModelO; - void run(const render::RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer); + using Output = gpu::FramebufferPointer; + using Config = PreparePrimaryFramebufferConfig; + using JobModel = render::Job::ModelO; + + PreparePrimaryFramebuffer(float resolutionScale = 1.0f) : _resolutionScale{resolutionScale} {} + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, Output& primaryFramebuffer); gpu::FramebufferPointer _primaryFramebuffer; + float _resolutionScale{ 1.0f }; + +private: + + static gpu::FramebufferPointer createFramebuffer(const char* name, const glm::uvec2& size); }; class PrepareDeferred { diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index e6337d7099..94bac4e3ac 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -107,11 +107,11 @@ void MakeHaze::run(const render::RenderContextPointer& renderContext, graphics:: haze = _haze; } +// Buffer slots const int HazeEffect_ParamsSlot = 0; const int HazeEffect_TransformBufferSlot = 1; -const int HazeEffect_ColorMapSlot = 2; -const int HazeEffect_LinearDepthMapSlot = 3; -const int HazeEffect_LightingMapSlot = 4; +// Texture slots +const int HazeEffect_LinearDepthMapSlot = 0; void DrawHaze::configure(const Config& config) { } @@ -122,11 +122,10 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu return; } - const auto inputBuffer = inputs.get1()->getRenderBuffer(0); + const auto outputBuffer = inputs.get1(); const auto framebuffer = inputs.get2(); const auto transformBuffer = inputs.get3(); - - auto outputBuffer = inputs.get4(); + const auto lightingModel = inputs.get4(); auto depthBuffer = framebuffer->getLinearDepthTexture(); @@ -139,6 +138,10 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + 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); + // Mask out haze on the tablet PrepareStencil::testMask(*state); @@ -148,15 +151,15 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), HazeEffect_ParamsSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), HazeEffect_TransformBufferSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("colorMap"), HazeEffect_ColorMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("lightingModelBuffer"), render::ShapePipeline::Slot::LIGHTING_MODEL)); slotBindings.insert(gpu::Shader::Binding(std::string("linearDepthMap"), HazeEffect_LinearDepthMapSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), HazeEffect_LightingMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), render::ShapePipeline::Slot::KEY_LIGHT)); gpu::Shader::makeProgram(*program, slotBindings); }); }); } - auto sourceFramebufferSize = glm::ivec2(inputBuffer->getDimensions()); + auto outputFramebufferSize = glm::ivec2(outputBuffer->getSize()); gpu::doInBatch("DrawHaze::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -165,7 +168,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(sourceFramebufferSize, args->_viewport)); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(outputFramebufferSize, args->_viewport)); batch.setPipeline(_hazePipeline); @@ -181,17 +184,17 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu } batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer()); + batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); auto lightStage = args->_scene->getStage(); if (lightStage) { graphics::LightPointer keyLight; keyLight = lightStage->getCurrentKeyLight(); if (keyLight) { - batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer()); + batch.setUniformBuffer(render::ShapePipeline::Slot::KEY_LIGHT, keyLight->getLightSchemaBuffer()); } } - batch.setResourceTexture(HazeEffect_ColorMapSlot, inputBuffer); batch.setResourceTexture(HazeEffect_LinearDepthMapSlot, depthBuffer); batch.draw(gpu::TRIANGLE_STRIP, 4); diff --git a/libraries/render-utils/src/DrawHaze.h b/libraries/render-utils/src/DrawHaze.h index e7d4e15d77..e30ce26dd4 100644 --- a/libraries/render-utils/src/DrawHaze.h +++ b/libraries/render-utils/src/DrawHaze.h @@ -22,6 +22,7 @@ #include #include "SurfaceGeometryPass.h" +#include "LightingModel.h" using LinearDepthFramebufferPointer = std::shared_ptr; @@ -159,7 +160,7 @@ public: class DrawHaze { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet5; using Config = HazeConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/Fade.slh b/libraries/render-utils/src/Fade.slh index a06c8c869e..a4e8fdf1f4 100644 --- a/libraries/render-utils/src/Fade.slh +++ b/libraries/render-utils/src/Fade.slh @@ -15,19 +15,20 @@ #define CATEGORY_COUNT 5 <@include Fade_shared.slh@> +<@include FadeObjectParams.shared.slh@> layout(std140) uniform fadeParametersBuffer { FadeParameters fadeParameters[CATEGORY_COUNT]; }; uniform sampler2D fadeMaskMap; -struct FadeObjectParams { - int category; - float threshold; - vec3 noiseOffset; - vec3 baseOffset; - vec3 baseInvSize; -}; +vec3 getNoiseInverseSize(int category) { + return fadeParameters[category]._noiseInvSizeAndLevel.xyz; +} + +float getNoiseLevel(int category) { + return fadeParameters[category]._noiseInvSizeAndLevel.w; +} vec2 hash2D(vec3 position) { return position.xy* vec2(0.1677, 0.221765) + position.z*0.561; @@ -40,7 +41,7 @@ float noise3D(vec3 position) { float evalFadeNoiseGradient(FadeObjectParams params, vec3 position) { // Do tri-linear interpolation - vec3 noisePosition = position * fadeParameters[params.category]._noiseInvSizeAndLevel.xyz + params.noiseOffset; + vec3 noisePosition = position * getNoiseInverseSize(params.category) + params.noiseOffset.xyz; vec3 noisePositionFloored = floor(noisePosition); vec3 noisePositionFraction = fract(noisePosition); @@ -61,11 +62,11 @@ float evalFadeNoiseGradient(FadeObjectParams params, vec3 position) { float noise = mix(maskY.x, maskY.y, noisePositionFraction.y); noise -= 0.5; // Center on value 0 - return noise * fadeParameters[params.category]._noiseInvSizeAndLevel.w; + return noise * getNoiseLevel(params.category); } float evalFadeBaseGradient(FadeObjectParams params, vec3 position) { - float gradient = length((position - params.baseOffset) * params.baseInvSize.xyz); + float gradient = length((position - params.baseOffset.xyz) * params.baseInvSize.xyz); gradient = gradient-0.5; // Center on value 0.5 gradient *= fadeParameters[params.category]._baseLevel; return gradient; @@ -112,20 +113,14 @@ void applyFade(FadeObjectParams params, vec3 position, out vec3 emissive) { <@func declareFadeFragmentUniform()@> -uniform int fadeCategory; -uniform vec3 fadeNoiseOffset; -uniform vec3 fadeBaseOffset; -uniform vec3 fadeBaseInvSize; -uniform float fadeThreshold; +layout(std140) uniform fadeObjectParametersBuffer { + FadeObjectParams fadeObjectParams; +}; <@endfunc@> <@func fetchFadeObjectParams(fadeParams)@> - <$fadeParams$>.category = fadeCategory; - <$fadeParams$>.threshold = fadeThreshold; - <$fadeParams$>.noiseOffset = fadeNoiseOffset; - <$fadeParams$>.baseOffset = fadeBaseOffset; - <$fadeParams$>.baseInvSize = fadeBaseInvSize; + <$fadeParams$> = fadeObjectParams; <@endfunc@> <@func declareFadeFragmentVertexInput()@> @@ -139,9 +134,9 @@ in vec4 _fadeData3; <@func fetchFadeObjectParamsInstanced(fadeParams)@> <$fadeParams$>.category = int(_fadeData1.w); <$fadeParams$>.threshold = _fadeData2.w; - <$fadeParams$>.noiseOffset = _fadeData1.xyz; - <$fadeParams$>.baseOffset = _fadeData2.xyz; - <$fadeParams$>.baseInvSize = _fadeData3.xyz; + <$fadeParams$>.noiseOffset = _fadeData1; + <$fadeParams$>.baseOffset = _fadeData2; + <$fadeParams$>.baseInvSize = _fadeData3; <@endfunc@> <@func declareFadeFragment()@> diff --git a/libraries/render-utils/src/FadeEffect.cpp b/libraries/render-utils/src/FadeEffect.cpp index c09aa99988..12531d4c9d 100644 --- a/libraries/render-utils/src/FadeEffect.cpp +++ b/libraries/render-utils/src/FadeEffect.cpp @@ -13,6 +13,8 @@ #include "render/TransitionStage.h" +#include "FadeObjectParams.shared.slh" + #include FadeEffect::FadeEffect() { @@ -31,15 +33,8 @@ void FadeEffect::build(render::Task::TaskConcept& task, const task::Varying& edi render::ShapePipeline::BatchSetter FadeEffect::getBatchSetter() const { return [this](const render::ShapePipeline& shapePipeline, gpu::Batch& batch, render::Args*) { - auto program = shapePipeline.pipeline->getProgram(); - auto maskMapLocation = program->getTextures().findLocation("fadeMaskMap"); - auto bufferLocation = program->getUniformBuffers().findLocation("fadeParametersBuffer"); - if (maskMapLocation != -1) { - batch.setResourceTexture(maskMapLocation, _maskMap); - } - if (bufferLocation != -1) { - batch.setUniformBuffer(bufferLocation, _configurations); - } + batch.setResourceTexture(render::ShapePipeline::Slot::FADE_MASK, _maskMap); + batch.setUniformBuffer(render::ShapePipeline::Slot::FADE_PARAMETERS, _configurations); }; } @@ -50,23 +45,29 @@ render::ShapePipeline::ItemSetter FadeEffect::getItemUniformSetter() const { auto batch = args->_batch; auto transitionStage = scene->getStage(render::TransitionStage::getName()); auto& transitionState = transitionStage->getTransition(item.getTransitionId()); - auto program = shapePipeline.pipeline->getProgram(); - auto& uniforms = program->getUniforms(); - auto fadeNoiseOffsetLocation = uniforms.findLocation("fadeNoiseOffset"); - auto fadeBaseOffsetLocation = uniforms.findLocation("fadeBaseOffset"); - auto fadeBaseInvSizeLocation = uniforms.findLocation("fadeBaseInvSize"); - auto fadeThresholdLocation = uniforms.findLocation("fadeThreshold"); - auto fadeCategoryLocation = uniforms.findLocation("fadeCategory"); - if (fadeNoiseOffsetLocation >= 0 || fadeBaseInvSizeLocation >= 0 || fadeBaseOffsetLocation >= 0 || fadeThresholdLocation >= 0 || fadeCategoryLocation >= 0) { - const auto fadeCategory = FadeJob::transitionToCategory[transitionState.eventType]; - - batch->_glUniform1i(fadeCategoryLocation, fadeCategory); - batch->_glUniform1f(fadeThresholdLocation, transitionState.threshold); - batch->_glUniform3f(fadeNoiseOffsetLocation, transitionState.noiseOffset.x, transitionState.noiseOffset.y, transitionState.noiseOffset.z); - batch->_glUniform3f(fadeBaseOffsetLocation, transitionState.baseOffset.x, transitionState.baseOffset.y, transitionState.baseOffset.z); - batch->_glUniform3f(fadeBaseInvSizeLocation, transitionState.baseInvSize.x, transitionState.baseInvSize.y, transitionState.baseInvSize.z); + if (transitionState.paramsBuffer._size != sizeof(gpu::StructBuffer)) { + static_assert(sizeof(transitionState.paramsBuffer) == sizeof(gpu::StructBuffer), "Assuming gpu::StructBuffer is a helper class for gpu::BufferView"); + transitionState.paramsBuffer = gpu::StructBuffer(); } + + const auto fadeCategory = FadeJob::transitionToCategory[transitionState.eventType]; + auto& paramsConst = static_cast&>(transitionState.paramsBuffer).get(); + + if (paramsConst.category != fadeCategory + || paramsConst.threshold != transitionState.threshold + || glm::vec3(paramsConst.baseOffset) != transitionState.baseOffset + || glm::vec3(paramsConst.noiseOffset) != transitionState.noiseOffset + || glm::vec3(paramsConst.baseInvSize) != transitionState.baseInvSize) { + auto& params = static_cast&>(transitionState.paramsBuffer).edit(); + + params.category = fadeCategory; + params.threshold = transitionState.threshold; + params.baseInvSize = glm::vec4(transitionState.baseInvSize, 0.0f); + params.noiseOffset = glm::vec4(transitionState.noiseOffset, 0.0f); + params.baseOffset = glm::vec4(transitionState.baseOffset, 0.0f); + } + batch->setUniformBuffer(render::ShapePipeline::Slot::FADE_OBJECT_PARAMETERS, transitionState.paramsBuffer); } }; } diff --git a/libraries/render-utils/src/FadeObjectParams.shared.slh b/libraries/render-utils/src/FadeObjectParams.shared.slh new file mode 100644 index 0000000000..e97acaf0b0 --- /dev/null +++ b/libraries/render-utils/src/FadeObjectParams.shared.slh @@ -0,0 +1,25 @@ +// glsl / C++ compatible source as interface for FadeObjectParams +#ifdef __cplusplus +# define FOP_VEC4 glm::vec4 +# define FOP_VEC2 glm::vec2 +# define FOP_FLOAT32 glm::float32 +# define FOP_INT32 glm::int32 +#else +# define FOP_VEC4 vec4 +# define FOP_VEC2 vec2 +# define FOP_FLOAT32 float +# define FOP_INT32 int +#endif + +struct FadeObjectParams { + FOP_VEC4 noiseOffset; + FOP_VEC4 baseOffset; + FOP_VEC4 baseInvSize; + FOP_INT32 category; + FOP_FLOAT32 threshold; +}; + + // <@if 1@> + // Trigger Scribe include + // <@endif@> +// diff --git a/libraries/render-utils/src/ForwardGlobalLight.slh b/libraries/render-utils/src/ForwardGlobalLight.slh index eccc44186c..cf5f070c55 100644 --- a/libraries/render-utils/src/ForwardGlobalLight.slh +++ b/libraries/render-utils/src/ForwardGlobalLight.slh @@ -228,15 +228,14 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze( // Haze // FIXME - temporarily removed until we support it for forward... /* if ((hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) { - vec4 colorV4 = computeHazeColor( - vec4(color, 1.0), // fragment original color + vec4 hazeColor = computeHazeColor( positionES, // fragment position in eye coordinates fragPositionWS, // fragment position in world coordinates invViewMat[3].xyz, // eye position in world coordinates lightDirection // keylight direction vector ); - color = colorV4.rgb; + color = mix(color.rgb, hazeColor.rgb, hazeColor.a); }*/ return color; diff --git a/libraries/render-utils/src/Haze.slf b/libraries/render-utils/src/Haze.slf index 6b45a72768..93b66d99ed 100644 --- a/libraries/render-utils/src/Haze.slf +++ b/libraries/render-utils/src/Haze.slf @@ -22,7 +22,6 @@ <@include Haze.slh@> -uniform sampler2D colorMap; uniform sampler2D linearDepthMap; vec4 unpackPositionFromZeye(vec2 texcoord) { @@ -46,7 +45,6 @@ void main(void) { discard; } - vec4 fragColor = texture(colorMap, varTexCoord0); vec4 fragPositionES = unpackPositionFromZeye(varTexCoord0); mat4 viewInverse = getViewInverse(); @@ -56,5 +54,8 @@ void main(void) { Light light = getKeyLight(); vec3 lightDirectionWS = getLightDirection(light); - outFragColor = computeHazeColor(fragColor, fragPositionES.xyz, fragPositionWS.xyz, eyePositionWS.xyz, lightDirectionWS); + outFragColor = computeHazeColor(fragPositionES.xyz, fragPositionWS.xyz, eyePositionWS.xyz, lightDirectionWS); + if (outFragColor.a < 1e-4) { + discard; + } } diff --git a/libraries/render-utils/src/Haze.slh b/libraries/render-utils/src/Haze.slh index ab973ba752..7854ad08ca 100644 --- a/libraries/render-utils/src/Haze.slh +++ b/libraries/render-utils/src/Haze.slh @@ -92,22 +92,21 @@ vec3 computeHazeColorKeyLightAttenuation(vec3 color, vec3 lightDirectionWS, vec3 } // Input: -// fragColor - fragment original color // fragPositionES - fragment position in eye coordinates // fragPositionWS - fragment position in world coordinates // eyePositionWS - eye position in world coordinates // Output: -// fragment colour after haze effect +// haze colour and alpha contains haze blend factor // // General algorithm taken from http://www.iquilezles.org/www/articles/fog/fog.htm, with permission // -vec4 computeHazeColor(vec4 fragColor, vec3 fragPositionES, vec3 fragPositionWS, vec3 eyePositionWS, vec3 lightDirectionWS) { +vec4 computeHazeColor(vec3 fragPositionES, vec3 fragPositionWS, vec3 eyePositionWS, vec3 lightDirectionWS) { // Distance to fragment float distance = length(fragPositionES); float eyeWorldHeight = eyePositionWS.y; // Convert haze colour from uniform into a vec4 - vec4 hazeColor = vec4(hazeParams.hazeColor, 1.0); + vec4 hazeColor = vec4(hazeParams.hazeColor, 1.0); // Use the haze colour for the glare colour, if blend is not enabled vec4 blendedHazeColor; @@ -149,13 +148,13 @@ vec4 computeHazeColor(vec4 fragColor, vec3 fragPositionES, vec3 fragPositionWS, vec3 hazeAmount = 1.0 - exp(-hazeIntegral); // Compute color after haze effect - potentialFragColor = mix(fragColor, vec4(1.0, 1.0, 1.0, 1.0), vec4(hazeAmount, 1.0)); + potentialFragColor = vec4(1.0, 1.0, 1.0, hazeAmount); } else if ((hazeParams.hazeMode & HAZE_MODE_IS_ALTITUDE_BASED) != HAZE_MODE_IS_ALTITUDE_BASED) { // Haze is based only on range float hazeAmount = 1.0 - exp(-distance * hazeParams.hazeRangeFactor); // Compute color after haze effect - potentialFragColor = mix(fragColor, blendedHazeColor, hazeAmount); + potentialFragColor = vec4(blendedHazeColor.rgb, hazeAmount); } else { // Haze is based on both range and altitude // Taken from www.crytek.com/download/GDC2007_RealtimeAtmoFxInGamesRev.ppt @@ -181,16 +180,14 @@ vec4 computeHazeColor(vec4 fragColor, vec3 fragPositionES, vec3 fragPositionWS, float hazeAmount = 1.0 - exp(-hazeIntegral); // Compute color after haze effect - potentialFragColor = mix(fragColor, blendedHazeColor, hazeAmount); + potentialFragColor = vec4(blendedHazeColor.rgb, hazeAmount); } // Mix with background at far range const float BLEND_DISTANCE = 27000.0f; - vec4 outFragColor; + vec4 outFragColor = potentialFragColor; if (distance > BLEND_DISTANCE) { - outFragColor = mix(potentialFragColor, fragColor, hazeParams.backgroundBlend); - } else { - outFragColor = potentialFragColor; + outFragColor.a *= hazeParams.backgroundBlend; } return outFragColor; diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 20d0cc39be..6c8a90da81 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -117,6 +117,9 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c assert(renderContext->args->hasViewFrustum()); auto& inShapes = inputs.get0(); + const int BOUNDS_SLOT = 0; + const int PARAMETERS_SLOT = 1; + if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(true, false, gpu::LESS_EQUAL); @@ -135,6 +138,8 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("ssbo0Buffer"), BOUNDS_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("parametersBuffer"), PARAMETERS_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); _stencilMaskPipeline = gpu::Pipeline::create(program, state); @@ -214,6 +219,15 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c _boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data()); + const auto securityMargin = 2.0f; + const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w); + const auto framebufferSize = ressources->getSourceFrameSize(); + const glm::vec2 highlightWidth = { blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y }; + + if (highlightWidth != _outlineWidth.get()) { + _outlineWidth.edit() = highlightWidth; + } + gpu::doInBatch("DrawHighlightMask::run::end", args->_context, [&](gpu::Batch& batch) { // Setup camera, projection and viewport for all items batch.setViewportTransform(args->_viewport); @@ -221,15 +235,10 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c batch.setViewTransform(viewMat); // Draw stencil mask with object bounding boxes - const auto highlightWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth"); - const auto securityMargin = 2.0f; - const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w); - const auto framebufferSize = ressources->getSourceFrameSize(); - auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline; batch.setPipeline(stencilPipeline); - batch.setResourceBuffer(0, _boundsBuffer); - batch._glUniform2f(highlightWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y); + batch.setResourceBuffer(BOUNDS_SLOT, _boundsBuffer); + batch.setUniformBuffer(PARAMETERS_SLOT, _outlineWidth); static const int NUM_VERTICES_PER_CUBE = 36; batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_CUBE * (gpu::uint32) itemBounds.size(), 0); }); diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h index 8af11da237..eee1c29cb7 100644 --- a/libraries/render-utils/src/HighlightEffect.h +++ b/libraries/render-utils/src/HighlightEffect.h @@ -127,6 +127,7 @@ protected: render::ShapePlumberPointer _shapePlumber; HighlightSharedParametersPointer _sharedParameters; gpu::BufferPointer _boundsBuffer; + gpu::StructBuffer _outlineWidth; static gpu::PipelinePointer _stencilMaskPipeline; static gpu::PipelinePointer _stencilMaskFillPipeline; diff --git a/libraries/render-utils/src/Highlight_aabox.slv b/libraries/render-utils/src/Highlight_aabox.slv index 4927db9610..2a87e00f94 100644 --- a/libraries/render-utils/src/Highlight_aabox.slv +++ b/libraries/render-utils/src/Highlight_aabox.slv @@ -40,7 +40,9 @@ ItemBound getItemBound(int i) { } #endif -uniform vec2 outlineWidth; +uniform parametersBuffer { + vec2 outlineWidth; +}; void main(void) { const vec3 UNIT_BOX_VERTICES[8] = vec3[8]( diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 24715f0afb..c2181b7613 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -51,19 +51,19 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& config->setNumDrawn((int)inItems.size()); emit config->numDrawnChanged(); + RenderArgs* args = renderContext->args; + + // Clear the framebuffer without stereo + // Needs to be distinct from the other batch because using the clear call + // while stereo is enabled triggers a warning + 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); + }); + } + if (!inItems.empty()) { - RenderArgs* args = renderContext->args; - - // Clear the framebuffer without stereo - // Needs to be distinct from the other batch because using the clear call - // while stereo is enabled triggers a warning - 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); - }); - } - // Render the items gpu::doInBatch("DrawOverlay3D::main", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 32bdad280c..0b05977265 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "RenderHifi.h" #include "RenderCommonTask.h" @@ -59,8 +60,14 @@ RenderDeferredTask::RenderDeferredTask() { } -void RenderDeferredTask::configure(const Config& config) -{ +void RenderDeferredTask::configure(const Config& config) { + // Propagate resolution scale to sub jobs who need it + auto preparePrimaryBufferConfig = config.getConfig("PreparePrimaryBuffer"); + auto upsamplePrimaryBufferConfig = config.getConfig("PrimaryBufferUpscale"); + assert(preparePrimaryBufferConfig); + assert(upsamplePrimaryBufferConfig); + preparePrimaryBufferConfig->setProperty("resolutionScale", config.resolutionScale); + upsamplePrimaryBufferConfig->setProperty("factor", 1.0f / config.resolutionScale); } const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, const char* selectionName, @@ -97,23 +104,22 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto jitter = task.addJob("JitterCam"); - // Prepare deferred, generate the shared Deferred Frame Transform + // GPU jobs: Start preparing the primary, deferred and lighting buffer + const auto scaledPrimaryFramebuffer = task.addJob("PreparePrimaryBuffer"); + + // Prepare deferred, generate the shared Deferred Frame Transform. Only valid with the scaled frame buffer const auto deferredFrameTransform = task.addJob("DeferredFrameTransform", jitter); const auto lightingModel = task.addJob("LightingModel"); - - - // GPU jobs: Start preparing the primary, deferred and lighting buffer - const auto primaryFramebuffer = task.addJob("PreparePrimaryBuffer"); const auto opaqueRangeTimer = task.addJob("BeginOpaqueRangeTimer", "DrawOpaques"); - const auto prepareDeferredInputs = PrepareDeferred::Inputs(primaryFramebuffer, lightingModel).asVarying(); + const auto prepareDeferredInputs = PrepareDeferred::Inputs(scaledPrimaryFramebuffer, lightingModel).asVarying(); const auto prepareDeferredOutputs = task.addJob("PrepareDeferred", prepareDeferredInputs); const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); const auto lightingFramebuffer = prepareDeferredOutputs.getN(1); // draw a stencil mask in hidden regions of the framebuffer. - task.addJob("PrepareStencil", primaryFramebuffer); + task.addJob("PrepareStencil", scaledPrimaryFramebuffer); // Render opaque objects in DeferredBuffer const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel, jitter).asVarying(); @@ -174,7 +180,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job task.addJob("DrawBackgroundDeferred", lightingModel); - const auto drawHazeInputs = render::Varying(DrawHaze::Inputs(hazeModel, lightingFramebuffer, linearDepthTarget, deferredFrameTransform, lightingFramebuffer)); + const auto drawHazeInputs = render::Varying(DrawHaze::Inputs(hazeModel, lightingFramebuffer, linearDepthTarget, deferredFrameTransform, lightingModel)); task.addJob("DrawHazeDeferred", drawHazeInputs); // Render transparent objects forward in LightingBuffer @@ -223,7 +229,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping - const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying(); + const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); task.addJob("ToneMapping", toneMappingInputs); { // Debug the bounds of the rendered items, still look at the zbuffer @@ -284,6 +290,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawZoneStack", deferredFrameTransform); } + // Upscale to finale resolution + const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", scaledPrimaryFramebuffer); + // Composite the HUD and HUD overlays task.addJob("HUD"); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index ab6ab177d2..1ce1682cf1 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -105,11 +105,13 @@ class RenderDeferredTaskConfig : public render::Task::Config { Q_OBJECT Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty) Q_PROPERTY(float fadeDuration MEMBER fadeDuration NOTIFY dirty) + Q_PROPERTY(float resolutionScale MEMBER resolutionScale NOTIFY dirty) Q_PROPERTY(bool debugFade MEMBER debugFade NOTIFY dirty) Q_PROPERTY(float debugFadePercent MEMBER debugFadePercent NOTIFY dirty) public: float fadeScale{ 0.5f }; float fadeDuration{ 3.0f }; + float resolutionScale{ 1.f }; float debugFadePercent{ 0.f }; bool debugFade{ false }; diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index 07f7367582..008234b437 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -81,3 +81,69 @@ void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::F batch.draw(gpu::TRIANGLE_STRIP, 4); }); } + +gpu::PipelinePointer Upsample::_pipeline; + +void Upsample::configure(const Config& config) { + _factor = config.factor; +} + +gpu::FramebufferPointer Upsample::getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer) { + if (_factor == 1.0f) { + return sourceFramebuffer; + } + + auto resampledFramebufferSize = glm::uvec2(glm::vec2(sourceFramebuffer->getSize()) * _factor); + + if (!_destinationFrameBuffer || resampledFramebufferSize != _destinationFrameBuffer->getSize()) { + _destinationFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("UpsampledOutput")); + + auto sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); + auto target = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), resampledFramebufferSize.x, resampledFramebufferSize.y, gpu::Texture::SINGLE_MIP, sampler); + _destinationFrameBuffer->setRenderBuffer(0, target); + } + return _destinationFrameBuffer; +} + +void Upsample::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + + resampledFrameBuffer = getResampledFrameBuffer(sourceFramebuffer); + if (resampledFrameBuffer != sourceFramebuffer) { + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false, false)); + _pipeline = gpu::Pipeline::create(program, state); + } + + const auto bufferSize = resampledFrameBuffer->getSize(); + glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + + gpu::doInBatch("Upsample::run", args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + + batch.setFramebuffer(resampledFrameBuffer); + + batch.setViewportTransform(viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setPipeline(_pipeline); + + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); + batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + + // Set full final viewport + args->_viewport = viewport; + } +} diff --git a/libraries/render/src/render/ResampleTask.h b/libraries/render/src/render/ResampleTask.h index da2b7b3537..25f9c6a3e9 100644 --- a/libraries/render/src/render/ResampleTask.h +++ b/libraries/render/src/render/ResampleTask.h @@ -36,6 +36,37 @@ namespace render { gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); }; + + class UpsampleConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float factor MEMBER factor NOTIFY dirty) + public: + + float factor{ 1.0f }; + + signals: + void dirty(); + }; + + class Upsample { + public: + using Config = UpsampleConfig; + using JobModel = Job::ModelIO; + + Upsample(float factor = 2.0f) : _factor{ factor } {} + + void configure(const Config& config); + void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer); + + protected: + + static gpu::PipelinePointer _pipeline; + + gpu::FramebufferPointer _destinationFrameBuffer; + float _factor{ 2.0f }; + + gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); + }; } #endif // hifi_render_ResampleTask_h diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 1ce58c49ae..8cd04f8067 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -95,6 +95,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT_MAP)); slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), Slot::MAP::FADE_MASK)); slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), Slot::BUFFER::FADE_PARAMETERS)); + slotBindings.insert(gpu::Shader::Binding(std::string("fadeObjectParametersBuffer"), Slot::BUFFER::FADE_OBJECT_PARAMETERS)); slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), Slot::BUFFER::HAZE_MODEL)); if (key.isTranslucent()) { @@ -124,6 +125,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p locations->lightAmbientMapUnit = program->getTextures().findLocation("skyboxMap"); locations->fadeMaskTextureUnit = program->getTextures().findLocation("fadeMaskMap"); locations->fadeParameterBufferUnit = program->getUniformBuffers().findLocation("fadeParametersBuffer"); + locations->fadeObjectParameterBufferUnit = program->getUniformBuffers().findLocation("fadeObjectParametersBuffer"); locations->hazeParameterBufferUnit = program->getUniformBuffers().findLocation("hazeBuffer"); if (key.isTranslucent()) { locations->lightClusterGridBufferUnit = program->getUniformBuffers().findLocation("clusterGridBuffer"); diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 7d87d98deb..10f1b757cc 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -240,6 +240,7 @@ public: LIGHT_AMBIENT_BUFFER, HAZE_MODEL, FADE_PARAMETERS, + FADE_OBJECT_PARAMETERS, LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, @@ -254,9 +255,9 @@ public: ROUGHNESS, OCCLUSION, SCATTERING, - FADE_MASK, LIGHT_AMBIENT_MAP = 10, + FADE_MASK, }; }; @@ -278,6 +279,7 @@ public: int lightAmbientMapUnit; int fadeMaskTextureUnit; int fadeParameterBufferUnit; + int fadeObjectParameterBufferUnit; int hazeParameterBufferUnit; int lightClusterGridBufferUnit; int lightClusterContentBufferUnit; diff --git a/libraries/render/src/render/Transition.h b/libraries/render/src/render/Transition.h index 622e6f69ce..30bda8aa2a 100644 --- a/libraries/render/src/render/Transition.h +++ b/libraries/render/src/render/Transition.h @@ -42,6 +42,8 @@ namespace render { glm::vec3 baseInvSize{ 1.f, 1.f, 1.f }; float threshold{ 0.f }; uint8_t isFinished{ 0 }; + + mutable gpu::BufferView paramsBuffer; }; typedef std::shared_ptr TransitionPointer; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index be419e8005..72918e33f6 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -75,11 +75,10 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound } } -bool AudioScriptingInterface::setStereoInput(bool stereo) { +void AudioScriptingInterface::setStereoInput(bool stereo) { if (_localAudioInterface) { QMetaObject::invokeMethod(_localAudioInterface, "setIsStereoInput", Q_ARG(bool, stereo)); } - return true; } bool AudioScriptingInterface::isStereoInput() { diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 843fa3e8f0..20ca977da1 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -54,9 +54,8 @@ protected: /**jsdoc * @function Audio.setStereoInput * @param {boolean} stereo - * @returns {boolean} */ - Q_INVOKABLE bool setStereoInput(bool stereo); + Q_INVOKABLE void setStereoInput(bool stereo); /**jsdoc * @function Audio.isStereoInput diff --git a/libraries/shared/src/PhysicsHelpers.cpp b/libraries/shared/src/PhysicsHelpers.cpp index b43d55020e..988af98c46 100644 --- a/libraries/shared/src/PhysicsHelpers.cpp +++ b/libraries/shared/src/PhysicsHelpers.cpp @@ -42,22 +42,27 @@ glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float time // Exponential map // google for "Practical Parameterization of Rotations Using the Exponential Map", F. Sebastian Grassia - float speed = glm::length(angularVelocity); + glm::vec3 axis = angularVelocity; + float angle = glm::length(axis) * timeStep; // limit the angular motion because the exponential approximation fails for large steps const float ANGULAR_MOTION_THRESHOLD = 0.5f * PI_OVER_TWO; - if (speed * timeStep > ANGULAR_MOTION_THRESHOLD) { - speed = ANGULAR_MOTION_THRESHOLD / timeStep; + if (angle > ANGULAR_MOTION_THRESHOLD) { + angle = ANGULAR_MOTION_THRESHOLD; } - glm::vec3 axis = angularVelocity; - if (speed < 0.001f) { - // use Taylor's expansions of sync function - axis *= (0.5f * timeStep - (timeStep * timeStep * timeStep) * (0.020833333333f * speed * speed)); + const float MIN_ANGLE = 0.001f; + if (angle < MIN_ANGLE) { + // for small angles use Taylor's expansion of sin(x): + // sin(x) = x - (x^3)/(3!) + ... + // where: x = angle/2 + // sin(angle/2) = angle/2 - (angle*angle*angle)/48 + // but (angle = speed * timeStep) and we want to normalize the axis by dividing by speed + // which gives us: + axis *= timeStep * (0.5f - 0.020833333333f * angle * angle); } else { - // sync(speed) = sin(c * speed)/t - axis *= (sinf(0.5f * speed * timeStep) / speed ); + axis *= (sinf(0.5f * angle) * timeStep / angle); } - return glm::quat(cosf(0.5f * speed * timeStep), axis.x, axis.y, axis.z); + return glm::quat(cosf(0.5f * angle), axis.x, axis.y, axis.z); } /* end Bullet code derivation*/ diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index de84782471..f692128fa3 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -159,6 +159,9 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See for(var bookmarkName in message.data.bookmarks) { var bookmark = message.data.bookmarks[bookmarkName]; + if (!bookmark.avatarEntites) { // ensure avatarEntites always exist + bookmark.avatarEntites = []; + } bookmark.avatarEntites.forEach(function(avatarEntity) { avatarEntity.properties.localRotationAngles = Quat.safeEulerAngles(avatarEntity.properties.localRotation) diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 00d7ad0491..38334f5523 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -11,7 +11,8 @@ TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, - TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity + TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity, + distanceBetweenEntityLocalPositionAndBoundingBox */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -172,12 +173,9 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * TEAR_AWAY_CHECK_TIME) { this.lastUnequipCheckTime = now; if (props.parentID === MyAvatar.SELF_ID) { - var sensorScaleFactor = MyAvatar.sensorToWorldScale; - var handPosition = controllerData.controllerLocations[this.hand].position; - var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props); - var distance = Vec3.distance(props.position, handPosition); - if ((dist > TEAR_AWAY_DISTANCE) || - (distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) { + var tearAwayDistance = TEAR_AWAY_DISTANCE * MyAvatar.sensorToWorldScale; + var distance = distanceBetweenEntityLocalPositionAndBoundingBox(props); + if (distance > tearAwayDistance) { this.autoUnequipCounter++; } else { this.autoUnequipCounter = 0; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 73088560d9..0789e1fac2 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -63,6 +63,15 @@ var createToolsWindow = new CreateWindow( false ); +/** + * @description Returns true in case we should use the tablet version of the CreateApp + * @returns boolean + */ +var shouldUseEditTabletApp = function() { + return HMD.active || (!HMD.active && !Settings.getValue("desktopTabletBecomesToolbar", true)); +}; + + var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; @@ -88,11 +97,12 @@ var cameraManager = new CameraManager(); var grid = new Grid(); var gridTool = new GridTool({ horizontalGrid: grid, - createToolsWindow: createToolsWindow + createToolsWindow: createToolsWindow, + shouldUseEditTabletApp: shouldUseEditTabletApp }); gridTool.setVisible(false); -var entityListTool = new EntityListTool(); +var entityListTool = new EntityListTool(shouldUseEditTabletApp); selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); @@ -578,7 +588,8 @@ var toolBar = (function () { }); createButton = activeButton; tablet.screenChanged.connect(function (type, url) { - var isGoingToHomescreenOnDesktop = (!HMD.active && (url === 'hifi/tablet/TabletHome.qml' || url === '')); + var isGoingToHomescreenOnDesktop = (!shouldUseEditTabletApp() && + (url === 'hifi/tablet/TabletHome.qml' || url === '')); if (isActive && (type !== "QML" || url !== "hifi/tablet/Edit.qml") && !isGoingToHomescreenOnDesktop) { that.setActive(false); } @@ -605,7 +616,7 @@ var toolBar = (function () { }); function createNewEntityDialogButtonCallback(entityType) { return function() { - if (HMD.active) { + if (shouldUseEditTabletApp()) { // tablet version of new-model dialog var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.pushOntoStack("hifi/tablet/New" + entityType + "Dialog.qml"); @@ -837,7 +848,7 @@ var toolBar = (function () { selectionDisplay.triggerMapping.disable(); tablet.landscape = false; } else { - if (HMD.active) { + if (shouldUseEditTabletApp()) { tablet.loadQMLSource("hifi/tablet/Edit.qml", true); } else { // make other apps inactive while in desktop mode @@ -1573,15 +1584,11 @@ function deleteSelectedEntities() { Entities.deleteEntity(entityID); } } - + if (savedProperties.length > 0) { SelectionManager.clearSelections(); pushCommandForSelections([], savedProperties); - - entityListTool.webView.emitScriptEvent(JSON.stringify({ - type: "deleted", - ids: deletedIDs - })); + entityListTool.deleteEntities(deletedIDs); } } } @@ -1853,13 +1860,7 @@ var keyReleaseEvent = function (event) { } } else if (event.text === 'g') { if (isActive && selectionManager.hasSelection()) { - var newPosition = selectionManager.worldPosition; - newPosition = Vec3.subtract(newPosition, { - x: 0, - y: selectionManager.worldDimensions.y * 0.5, - z: 0 - }); - grid.setPosition(newPosition); + grid.moveToSelection(); } } else if (event.key === KEY_P && event.isControl && !event.isAutoRepeat ) { if (event.isShifted) { @@ -1999,8 +2000,8 @@ var PropertiesTool = function (opts) { that.setVisible = function (newVisible) { visible = newVisible; - webView.setVisible(HMD.active && visible); - createToolsWindow.setVisible(!HMD.active && visible); + webView.setVisible(shouldUseEditTabletApp() && visible); + createToolsWindow.setVisible(!shouldUseEditTabletApp() && visible); }; that.setVisible(false); @@ -2426,7 +2427,7 @@ function selectParticleEntity(entityID) { // Switch to particle explorer var selectTabMethod = { method: 'selectTab', params: { id: 'particle' } }; - if (HMD.active) { + if (shouldUseEditTabletApp()) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.sendToQml(selectTabMethod); } else { diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 5dfb0d5b69..e817bb4ee1 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -59,6 +59,7 @@ highlightTargetEntity:true, clearHighlightedEntities:true, unhighlightTargetEntity:true + distanceBetweenEntityLocalPositionAndBoundingBox: true */ MSECS_PER_SEC = 1000.0; @@ -130,7 +131,9 @@ DISPATCHER_PROPERTIES = [ "type", "href", "cloneable", - "cloneDynamic" + "cloneDynamic", + "localPosition", + "localRotation" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step @@ -413,6 +416,25 @@ findHandChildEntities = function(hand) { }); }; +distanceBetweenEntityLocalPositionAndBoundingBox = function(entityProps) { + var localPoint = entityProps.localPosition; + var entityXform = new Xform(entityProps.rotation, entityProps.position); + var minOffset = Vec3.multiplyVbyV(entityProps.registrationPoint, entityProps.dimensions); + var maxOffset = Vec3.multiplyVbyV(Vec3.subtract(ONE_VEC, entityProps.registrationPoint), entityProps.dimensions); + var localMin = Vec3.subtract(entityXform.trans, minOffset); + var localMax = Vec3.sum(entityXform.trans, maxOffset); + + var v = {x: localPoint.x, y: localPoint.y, z: localPoint.z}; + v.x = Math.max(v.x, localMin.x); + v.x = Math.min(v.x, localMax.x); + v.y = Math.max(v.y, localMin.y); + v.y = Math.min(v.y, localMax.y); + v.z = Math.max(v.z, localMin.z); + v.z = Math.min(v.z, localMax.z); + + return Vec3.distance(v, localPoint); +}; + distanceBetweenPointAndEntityBoundingBox = function(point, entityProps) { var entityXform = new Xform(entityProps.rotation, entityProps.position); var localPoint = entityXform.inv().xformPoint(point); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index de8e5d9c06..fb876302dd 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -11,7 +11,7 @@ /* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages, cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible */ -EntityListTool = function() { +EntityListTool = function(shouldUseEditTabletApp) { var that = {}; var CreateWindow = Script.require('../modules/createWindow.js'); @@ -55,8 +55,8 @@ EntityListTool = function() { that.setVisible = function(newVisible) { visible = newVisible; - webView.setVisible(HMD.active && visible); - entityListWindow.setVisible(!HMD.active && visible); + webView.setVisible(shouldUseEditTabletApp() && visible); + entityListWindow.setVisible(!shouldUseEditTabletApp() && visible); }; that.setVisible(false); @@ -93,12 +93,18 @@ EntityListTool = function() { }; that.removeEntities = function (deletedIDs, selectedIDs) { - var data = { + emitJSONScriptEvent({ type: 'removeEntities', deletedIDs: deletedIDs, selectedIDs: selectedIDs - }; - webView.emitScriptEvent(JSON.stringify(data)); + }); + }; + + that.deleteEntities = function (deletedIDs) { + emitJSONScriptEvent({ + type: "deleted", + ids: deletedIDs + }); }; function valueIfDefined(value) { diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 4ff139ee75..c9a15875f1 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1186,10 +1186,17 @@ SelectionDisplay = (function() { var localRotationZ = Quat.fromPitchYawRollDegrees(rotationDegrees, 0, 0); var rotationZ = Quat.multiply(rotation, localRotationZ); worldRotationZ = rotationZ; + + var selectionBoxGeometry = { + position: position, + rotation: rotation, + dimensions: dimensions + }; + var isCameraInsideBox = isPointInsideBox(Camera.position, selectionBoxGeometry); - // in HMD we clamp the overlays to the bounding box for now so lasers can hit them + // in HMD if outside the bounding box clamp the overlays to the bounding box for now so lasers can hit them var maxHandleDimension = 0; - if (HMD.active) { + if (HMD.active && !isCameraInsideBox) { maxHandleDimension = Math.max(dimensions.x, dimensions.y, dimensions.z); } @@ -1438,12 +1445,6 @@ SelectionDisplay = (function() { var inModeRotate = isActiveTool(handleRotatePitchRing) || isActiveTool(handleRotateYawRing) || isActiveTool(handleRotateRollRing); - var selectionBoxGeometry = { - position: position, - rotation: rotation, - dimensions: dimensions - }; - var isCameraInsideBox = isPointInsideBox(Camera.position, selectionBoxGeometry); selectionBoxGeometry.visible = !inModeRotate && !isCameraInsideBox; Overlays.editOverlay(selectionBox, selectionBoxGeometry); diff --git a/scripts/system/libraries/gridTool.js b/scripts/system/libraries/gridTool.js index 690b4eb4b9..3a114f23c7 100644 --- a/scripts/system/libraries/gridTool.js +++ b/scripts/system/libraries/gridTool.js @@ -1,6 +1,6 @@ var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html'); -Grid = function(opts) { +Grid = function() { var that = {}; var gridColor = { red: 0, green: 0, blue: 0 }; var gridAlpha = 0.6; @@ -154,6 +154,12 @@ Grid = function(opts) { that.emitUpdate(); } }; + + that.moveToSelection = function() { + var newPosition = SelectionManager.worldPosition; + newPosition = Vec3.subtract(newPosition, { x: 0, y: SelectionManager.worldDimensions.y * 0.5, z: 0 }); + that.setPosition(newPosition); + }; that.emitUpdate = function() { if (that.onUpdate) { @@ -241,6 +247,7 @@ GridTool = function(opts) { var horizontalGrid = opts.horizontalGrid; var verticalGrid = opts.verticalGrid; var createToolsWindow = opts.createToolsWindow; + var shouldUseEditTabletApp = opts.shouldUseEditTabletApp; var listeners = []; var webView = null; @@ -280,9 +287,7 @@ GridTool = function(opts) { } horizontalGrid.setPosition(position); } else if (action == "moveToSelection") { - var newPosition = selectionManager.worldPosition; - newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 }); - grid.setPosition(newPosition); + horizontalGrid.moveToSelection(); } } }; @@ -295,7 +300,7 @@ GridTool = function(opts) { }; that.setVisible = function(visible) { - webView.setVisible(HMD.active && visible); + webView.setVisible(shouldUseEditTabletApp() && visible); }; return that; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7175685b4f..fcff83a3d3 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -19,7 +19,7 @@ var populateNearbyUserList, color, textures, removeOverlays, controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged, receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL, - createAudioInterval, tablet, CHANNEL, getConnectionData, findableByChanged, + tablet, CHANNEL, getConnectionData, findableByChanged, avatarAdded, avatarRemoved, avatarSessionChanged; // forward references; // hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed @@ -447,21 +447,24 @@ function populateNearbyUserList(selectData, oldAudioData) { verticalAngleNormal = filter && Quat.getRight(orientation), horizontalAngleNormal = filter && Quat.getUp(orientation); avatarsOfInterest = {}; - avatars.forEach(function (id) { - var avatar = AvatarList.getAvatar(id); - var name = avatar.sessionDisplayName; + + var avatarData = AvatarList.getPalData().data; + + avatarData.forEach(function (currentAvatarData) { + var id = currentAvatarData.sessionUUID; + var name = currentAvatarData.sessionDisplayName; if (!name) { // Either we got a data packet but no identity yet, or something is really messed up. In any case, // we won't be able to do anything with this user, so don't include them. // In normal circumstances, a refresh will bring in the new user, but if we're very heavily loaded, // we could be losing and gaining people randomly. - print('No avatar identity data for', id); + print('No avatar identity data for', currentAvatarData.sessionUUID); return; } - if (id && myPosition && (Vec3.distance(avatar.position, myPosition) > filter.distance)) { + if (id && myPosition && (Vec3.distance(currentAvatarData.position, myPosition) > filter.distance)) { return; } - var normal = id && filter && Vec3.normalize(Vec3.subtract(avatar.position, myPosition)); + var normal = id && filter && Vec3.normalize(Vec3.subtract(currentAvatarData.position, myPosition)); var horizontal = normal && angleBetweenVectorsInPlane(normal, forward, horizontalAngleNormal); var vertical = normal && angleBetweenVectorsInPlane(normal, forward, verticalAngleNormal); if (id && filter && ((Math.abs(horizontal) > horizontalHalfAngle) || (Math.abs(vertical) > verticalHalfAngle))) { @@ -480,11 +483,11 @@ function populateNearbyUserList(selectData, oldAudioData) { personalMute: !!id && Users.getPersonalMuteStatus(id), // expects proper boolean, not null ignore: !!id && Users.getIgnoreStatus(id), // ditto isPresent: true, - isReplicated: avatar.isReplicated + isReplicated: currentAvatarData.isReplicated }; // Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin. Users.requestUsernameFromID(id); - if (id) { + if (id !== "") { addAvatarNode(id); // No overlay for ourselves avatarsOfInterest[id] = true; } else { @@ -515,30 +518,63 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { updateUser(data); } +function updateAudioLevel(avatarData) { + // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged + // But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency + // of updating (the latter for efficiency too). + var audioLevel = 0.0; + var avgAudioLevel = 0.0; + + var data = avatarData.sessionUUID === "" ? myData : ExtendedOverlay.get(avatarData.sessionUUID); + + if (data) { + // we will do exponential moving average by taking some the last loudness and averaging + data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatarData.audioLoudness); + + // add 1 to insure we don't go log() and hit -infinity. Math.log is + // natural log, so to get log base 2, just divide by ln(2). + audioLevel = scaleAudio(Math.log(data.accumulatedLevel + 1) / LOG2); + + // decay avgAudioLevel + avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (data.avgAudioLevel || 0), audioLevel); + + data.avgAudioLevel = avgAudioLevel; + data.audioLevel = audioLevel; + + // now scale for the gain. Also, asked to boost the low end, so one simple way is + // to take sqrt of the value. Lets try that, see how it feels. + avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[avatarData.sessionUUID] || 0.75))); + } + + var param = {}; + var level = [audioLevel, avgAudioLevel]; + var userId = avatarData.sessionUUID; + param[userId] = level; + sendToQml({ method: 'updateAudioLevel', params: param }); +} + var pingPong = true; function updateOverlays() { var eye = Camera.position; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - if (!id || !avatarsOfInterest[id]) { + + var avatarData = AvatarList.getPalData().data; + + avatarData.forEach(function (currentAvatarData) { + + if (currentAvatarData.sessionUUID === "" || !avatarsOfInterest[currentAvatarData.sessionUUID]) { return; // don't update ourself, or avatars we're not interested in } - var avatar = AvatarList.getAvatar(id); - if (!avatar) { - return; // will be deleted below if there had been an overlay. - } - var overlay = ExtendedOverlay.get(id); + updateAudioLevel(currentAvatarData); + var overlay = ExtendedOverlay.get(currentAvatarData.sessionUUID); if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. - print('Adding non-PAL avatar node', id); - overlay = addAvatarNode(id); + print('Adding non-PAL avatar node', currentAvatarData.sessionUUID); + overlay = addAvatarNode(currentAvatarData.sessionUUID); } - var target = avatar.position; + + var target = currentAvatarData.position; var distance = Vec3.distance(target, eye); - var offset = 0.2; + var offset = currentAvatarData.palOrbOffset; var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) - var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can - if (headIndex > 0) { - offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; - } // move a bit in front, towards the camera target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); @@ -548,7 +584,7 @@ function updateOverlays() { overlay.ping = pingPong; overlay.editOverlay({ - color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel), + color: color(ExtendedOverlay.isSelected(currentAvatarData.sessionUUID), overlay.hovering, overlay.audioLevel), position: target, dimensions: 0.032 * distance }); @@ -670,12 +706,13 @@ triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Cont triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); function tabletVisibilityChanged() { - if (!tablet.tabletShown) { + if (!tablet.tabletShown && onPalScreen) { ContextOverlay.enabled = true; tablet.gotoHomeScreen(); } } +var wasOnPalScreen = false; var onPalScreen = false; var PAL_QML_SOURCE = "hifi/Pal.qml"; function onTabletButtonClicked() { @@ -703,9 +740,17 @@ function wireEventBridge(on) { hasEventBridge = false; } } + } + +var UPDATE_INTERVAL_MS = 100; +function createUpdateInterval() { + return Script.setInterval(function () { + updateOverlays(); + }, UPDATE_INTERVAL_MS); } function onTabletScreenChanged(type, url) { + wasOnPalScreen = onPalScreen; onPalScreen = (type === "QML" && url === PAL_QML_SOURCE); wireEventBridge(onPalScreen); // for toolbar mode: change button to active when window is first openend, false otherwise. @@ -717,10 +762,8 @@ function onTabletScreenChanged(type, url) { ContextOverlay.enabled = false; Users.requestsDomainListData = true; - audioTimer = createAudioInterval(AUDIO_LEVEL_UPDATE_INTERVAL_MS); - tablet.tabletShownChanged.connect(tabletVisibilityChanged); - Script.update.connect(updateOverlays); + updateInterval = createUpdateInterval(); Controller.mousePressEvent.connect(handleMouseEvent); Controller.mouseMoveEvent.connect(handleMouseMoveEvent); Users.usernameFromIDReply.connect(usernameFromIDReply); @@ -729,7 +772,9 @@ function onTabletScreenChanged(type, url) { populateNearbyUserList(); } else { off(); - ContextOverlay.enabled = true; + if (wasOnPalScreen) { + ContextOverlay.enabled = true; + } } } @@ -774,50 +819,6 @@ function scaleAudio(val) { return audioLevel; } -function getAudioLevel(id) { - // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged - // But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency - // of updating (the latter for efficiency too). - var avatar = AvatarList.getAvatar(id); - var audioLevel = 0.0; - var avgAudioLevel = 0.0; - var data = id ? ExtendedOverlay.get(id) : myData; - if (data) { - - // we will do exponential moving average by taking some the last loudness and averaging - data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness); - - // add 1 to insure we don't go log() and hit -infinity. Math.log is - // natural log, so to get log base 2, just divide by ln(2). - audioLevel = scaleAudio(Math.log(data.accumulatedLevel + 1) / LOG2); - - // decay avgAudioLevel - avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (data.avgAudioLevel || 0), audioLevel); - - data.avgAudioLevel = avgAudioLevel; - data.audioLevel = audioLevel; - - // now scale for the gain. Also, asked to boost the low end, so one simple way is - // to take sqrt of the value. Lets try that, see how it feels. - avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[id] || 0.75))); - } - return [audioLevel, avgAudioLevel]; -} - -function createAudioInterval(interval) { - // we will update the audioLevels periodically - // TODO: tune for efficiency - expecially with large numbers of avatars - return Script.setInterval(function () { - var param = {}; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - var level = getAudioLevel(id), - userId = id || 0; // qml didn't like an object with null/empty string for a key, so... - param[userId] = level; - }); - sendToQml({method: 'updateAudioLevel', params: param}); - }, interval); -} - function avatarDisconnected(nodeID) { // remove from the pal list sendToQml({method: 'avatarDisconnected', params: [nodeID]}); @@ -870,11 +871,11 @@ startup(); var isWired = false; -var audioTimer; -var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) function off() { if (isWired) { - Script.update.disconnect(updateOverlays); + if (updateInterval) { + Script.clearInterval(updateInterval); + } Controller.mousePressEvent.disconnect(handleMouseEvent); Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); tablet.tabletShownChanged.disconnect(tabletVisibilityChanged); @@ -885,10 +886,6 @@ function off() { Users.requestsDomainListData = false; isWired = false; - - if (audioTimer) { - Script.clearInterval(audioTimer); - } } removeOverlays(); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index c4fcb70792..3ddbeb997d 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -285,7 +285,7 @@ function printToPolaroid(image_url) { var polaroid_url = image_url; var model_pos = Vec3.sum(MyAvatar.position, Vec3.multiply(1.25, Quat.getForward(MyAvatar.orientation))); - model_pos.y += 0.2; // Print a bit closer to the head + model_pos.y += 0.39; // Print a bit closer to the head var model_q1 = MyAvatar.orientation; var model_q2 = Quat.angleAxis(90, Quat.getRight(model_q1)); @@ -307,10 +307,8 @@ function printToPolaroid(image_url) { "density": 200, "restitution": 0.15, - "gravity": { "x": 0, "y": -2.5, "z": 0 }, - - "velocity": { "x": 0, "y": 1.95, "z": 0 }, - "angularVelocity": Vec3.multiplyQbyV(MyAvatar.orientation, { "x": -1.0, "y": 0, "z": -1.3 }), + "gravity": { "x": 0, "y": -2.0, "z": 0 }, + "damping": 0.45, "dynamic": true, "collisionsWillMove": true, diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index 0db70f6fe4..ff672d13bf 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -31,8 +31,8 @@ BakerCLI::BakerCLI(OvenCLIApplication* parent) : QObject(parent) { void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& type) { // if the URL doesn't have a scheme, assume it is a local file - if (inputUrl.scheme() != "http" && inputUrl.scheme() != "https" && inputUrl.scheme() != "ftp") { - inputUrl.setScheme("file"); + if (inputUrl.scheme() != "http" && inputUrl.scheme() != "https" && inputUrl.scheme() != "ftp" && inputUrl.scheme() != "file") { + inputUrl = QUrl::fromLocalFile(inputUrl.toString()); } qDebug() << "Baking file type: " << type; diff --git a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml index e0c836fb1c..033039b87d 100644 --- a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml +++ b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml @@ -60,7 +60,7 @@ Rectangle { // "Spectator" text HifiStylesUit.RalewaySemiBold { id: titleBarText; - text: "Spectator Camera 2.2"; + text: "Spectator Camera 2.3"; // Anchors anchors.left: parent.left; anchors.leftMargin: 30; diff --git a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js index 3e749e38a2..4c39c5fb95 100644 --- a/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js +++ b/unpublishedScripts/marketplace/spectator-camera/spectatorCamera.js @@ -74,6 +74,7 @@ "collisionMask": 7, "dynamic": false, "modelURL": Script.resolvePath("spectator-camera.fbx"), + "name": "Spectator Camera", "registrationPoint": { "x": 0.56, "y": 0.545, @@ -102,6 +103,18 @@ position: cameraPosition, localOnly: true }); + + // Remove the existing camera model from the domain if one exists. + // It's easy for this to happen if the user crashes while the Spectator Camera is on. + // We do this down here (after the new one is rezzed) so that we don't accidentally delete + // the newly-rezzed model. + var entityIDs = Entities.findEntitiesByName("Spectator Camera", MyAvatar.position, 100, false); + entityIDs.forEach(function (currentEntityID) { + var currentEntityOwner = Entities.getEntityProperties(currentEntityID, ['owningAvatarID']).owningAvatarID; + if (currentEntityOwner === MyAvatar.sessionUUID && currentEntityID !== camera) { + Entities.deleteEntity(currentEntityID); + } + }); } // Function Name: spectatorCameraOff()