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/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/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/crashpad/CMakeLists.txt b/cmake/externals/crashpad/CMakeLists.txt index e509e115e4..648ec83280 100644 --- a/cmake/externals/crashpad/CMakeLists.txt +++ b/cmake/externals/crashpad/CMakeLists.txt @@ -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 http://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/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/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/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..0a69b8b3b5 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; } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 8a5b1fb0e7..16ad01a56d 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: { 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 5980f46523..0ad27e0dd3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1387,8 +1387,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. @@ -1440,13 +1438,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 @@ -2201,6 +2198,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(); @@ -3167,6 +3165,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"; @@ -3181,9 +3180,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); @@ -3209,13 +3212,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 = _graphicsEngine.getRenderEngine()->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, @@ -3227,7 +3239,6 @@ void Application::resizeGL() { } DependencyManager::get()->resize(fromGlm(displayPlugin->getRecommendedUiSize())); - displayPlugin->setRenderResolutionScale(renderResolutionScale); } void Application::handleSandboxStatus(QNetworkReply* reply) { @@ -8284,6 +8295,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); @@ -8298,6 +8316,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 4a36cd5b41..fb8edf3b86 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -320,6 +320,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/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/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/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index f256b0dfb5..21c9a9b22d 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -237,7 +237,7 @@ void GraphicsEngine::render_performFrame() { 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, qApp->getDeviceSize() * qApp->getRenderResolutionScale()); + renderArgs._viewport = glm::ivec4(0, 0, qApp->getDeviceSize()); qApp->getApplicationOverlay().renderOverlay(&renderArgs); } 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 a0b4f856a1..f1f64ec98d 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/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/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/gpu/src/gpu/Buffer.cpp b/libraries/gpu/src/gpu/Buffer.cpp index ebb768e597..d085b4df76 100644 --- a/libraries/gpu/src/gpu/Buffer.cpp +++ b/libraries/gpu/src/gpu/Buffer.cpp @@ -163,6 +163,31 @@ Buffer::Size Buffer::getSize() const { const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW ); + +BufferPointer _buffer; +Size _offset{ 0 }; +Size _size{ 0 }; +Element _element{ DEFAULT_ELEMENT }; +uint16 _stride{ 0 }; + +BufferView::BufferView(const BufferView& view) : + _buffer(view._buffer), + _offset(view._offset), + _size(view._size), + _element(view._element), + _stride(view._stride) +{} + +BufferView& BufferView::operator=(const BufferView& view) { + _buffer = (view._buffer); + _offset = (view._offset); + _size = (view._size); + _element = (view._element); + _stride = (view._stride); + + return (*this); +} + BufferView::BufferView() : BufferView(DEFAULT_ELEMENT) {} diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 01cc652fd1..e486e2392a 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -183,8 +183,8 @@ public: Element _element { DEFAULT_ELEMENT }; uint16 _stride { 0 }; - BufferView(const BufferView& view) = default; - BufferView& operator=(const BufferView& view) = default; + BufferView(const BufferView& view); + BufferView& operator=(const BufferView& view); BufferView(); BufferView(const Element& element); 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/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/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 2ac8e77898..5be05da505 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -176,7 +176,7 @@ AmbientOcclusionEffect::AmbientOcclusionEffect() { } void AmbientOcclusionEffect::configure(const Config& config) { - DependencyManager::get()->setAmbientOcclusionEnabled(config.enabled); + DependencyManager::get()->setAmbientOcclusionEnabled(config.isEnabled()); bool shouldUpdateGaussian = false; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index b3a93ab1de..69b229d39f 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -56,7 +56,6 @@ using AmbientOcclusionFramebufferPointer = std::shared_ptr + // 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 b198442b15..93d2b5177b 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 136bc2dabf..48ddfbf5cd 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 96d3f42958..fc4abb28c4 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-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 19ffcb4234..1c6291b1e0 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -36,7 +36,6 @@ protected: class RenderShadowTaskConfig : public render::Task::Config::Persistent { Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) public: RenderShadowTaskConfig() : render::Task::Config::Persistent(QStringList() << "Render" << "Engine" << "Shadows", true) {} diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMappingEffect.h index 046e7606b3..69694b13f5 100644 --- a/libraries/render-utils/src/ToneMappingEffect.h +++ b/libraries/render-utils/src/ToneMappingEffect.h @@ -64,7 +64,6 @@ private: class ToneMappingConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled) Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure); Q_PROPERTY(int curve MEMBER curve WRITE setCurve); public: 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/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/developer/utilities/render/engineProfiler.js b/scripts/developer/utilities/render/engineProfiler.js index 1baeb84dee..418cab8622 100644 --- a/scripts/developer/utilities/render/engineProfiler.js +++ b/scripts/developer/utilities/render/engineProfiler.js @@ -34,11 +34,11 @@ function createWindow() { var qml = Script.resolvePath(QMLAPP_URL); - window = new OverlayWindow({ + window = Desktop.createWindow(Script.resolvePath(QMLAPP_URL), { title: 'Render Engine Profiler', - source: qml, - width: 500, - height: 100 + flags: Desktop.ALWAYS_ON_TOP, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 500, y: 100} }); window.setPosition(200, 50); window.closed.connect(killWindow); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7175685b4f..e967ee6469 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -670,12 +670,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() { @@ -706,6 +707,7 @@ function wireEventBridge(on) { } 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. @@ -729,7 +731,9 @@ function onTabletScreenChanged(type, url) { populateNearbyUserList(); } else { off(); - ContextOverlay.enabled = true; + if (wasOnPalScreen) { + ContextOverlay.enabled = true; + } } } 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/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()