diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 4f64bf8f7f..971e9ed272 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -9,22 +9,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include #include "AssignmentClientApp.h" -#include int main(int argc, char* argv[]) { - disableQtBearerPoll(); // Fixes wifi ping spikes - - QCoreApplication::setApplicationName(BuildInfo::ASSIGNMENT_CLIENT_NAME); - QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); - QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); - QCoreApplication::setApplicationVersion(BuildInfo::VERSION); - - qInstallMessageHandler(LogHandler::verboseMessageHandler); - qInfo() << "Starting."; + setupHifiApplication(BuildInfo::ASSIGNMENT_CLIENT_NAME); AssignmentClientApp app(argc, argv); diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index dc3ee54fe7..d7856bf867 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -22,22 +22,10 @@ #include "DomainServer.h" int main(int argc, char* argv[]) { - disableQtBearerPoll(); // Fixes wifi ping spikes - - QCoreApplication::setApplicationName(BuildInfo::DOMAIN_SERVER_NAME); - QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); - QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); - QCoreApplication::setApplicationVersion(BuildInfo::VERSION); + setupHifiApplication(BuildInfo::DOMAIN_SERVER_NAME); Setting::init(); -#ifndef WIN32 - setvbuf(stdout, NULL, _IOLBF, 0); -#endif - - qInstallMessageHandler(LogHandler::verboseMessageHandler); - qInfo() << "Starting."; - int currentExitCode = 0; // use a do-while to handle domain-server restart diff --git a/ice-server/src/main.cpp b/ice-server/src/main.cpp index ec8b9957cf..aac6cc0422 100644 --- a/ice-server/src/main.cpp +++ b/ice-server/src/main.cpp @@ -11,18 +11,13 @@ #include -#include +#include #include "IceServer.h" int main(int argc, char* argv[]) { -#ifndef WIN32 - setvbuf(stdout, NULL, _IOLBF, 0); -#endif - - qInstallMessageHandler(LogHandler::verboseMessageHandler); - qInfo() << "Starting."; + setupHifiApplication("Ice Server"); IceServer iceServer(argc, argv); return iceServer.exec(); -} \ No newline at end of file +} diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 8db0377f88..7f7393da18 100644 Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index a3e4113d08..3c1d0fcd3c 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -22,6 +22,10 @@ TableView { 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 { } @@ -69,36 +73,39 @@ TableView { 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 { - left: parent.left - leftMargin: hifi.dimensions.tablePadding - right: parent.right - rightMargin: hifi.dimensions.tablePadding - verticalCenter: parent.verticalCenter - } + 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 { - left: titleText.right - leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 15 : 10) - right: parent.right - rightMargin: hifi.dimensions.tablePadding - verticalCenter: titleText.verticalCenter - } + 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 { @@ -152,7 +159,7 @@ TableView { color: styleData.selected ? hifi.colors.primaryHighlight : tableView.isLightColorScheme - ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) - : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) + ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) + : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) } } diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index dd0aba9ec5..fea275999e 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -206,7 +206,7 @@ Windows.ScrollingWindow { SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND; var DYNAMIC_DEFAULT = false; var prompt = desktop.customInputDialog({ textInput: { diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index bb54ba9e57..8fb27714ee 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -30,7 +30,7 @@ Rectangle { property int myCardWidth: width - upperRightInfoContainer.width; property int myCardHeight: 100; property int rowHeight: 60; - property int actionButtonWidth: 55; + property int actionButtonWidth: 65; property int locationColumnWidth: 170; property int nearbyNameCardWidth: nearbyTable.width - (iAmAdmin ? (actionButtonWidth * 4) : (actionButtonWidth * 2)) - 4 - hifi.dimensions.scrollbarBackgroundWidth; property int connectionsNameCardWidth: connectionsTable.width - locationColumnWidth - actionButtonWidth - 4 - hifi.dimensions.scrollbarBackgroundWidth; @@ -415,6 +415,7 @@ Rectangle { movable: false; resizable: false; } + TableViewColumn { role: "ignore"; title: "IGNORE"; @@ -599,13 +600,23 @@ Rectangle { } // This Rectangle refers to the [?] popup button next to "NAMES" Rectangle { + id: questionRect color: hifi.colors.tableBackgroundLight; width: 20; height: hifi.dimensions.tableHeaderHeight - 2; anchors.left: nearbyTable.left; anchors.top: nearbyTable.top; anchors.topMargin: 1; - anchors.leftMargin: actionButtonWidth + nearbyNameCardWidth/2 + displayNameHeaderMetrics.width/2 + 6; + + Connections { + target: nearbyTable + onTitlePaintedPosSignal: { + if (column === 1) { // name column + questionRect.anchors.leftMargin = actionButtonWidth + nearbyTable.titlePaintedPos[column] + } + } + } + RalewayRegular { id: helpText; text: "[?]"; diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index ab47bb28ad..d88ded6a15 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -48,6 +48,7 @@ Rectangle { property bool debugCheckoutSuccess: false; property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property string referrer; + property bool isInstalled; // Style color: hifi.colors.white; Connections { @@ -122,6 +123,12 @@ Rectangle { root.refreshBuyUI(); } } + + onAppInstalled: { + if (appHref === root.itemHref) { + root.isInstalled = true; + } + } } onItemIdChanged: { @@ -146,7 +153,8 @@ Rectangle { } onItemTypeChanged: { - if (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "avatar") { + if (root.itemType === "entity" || root.itemType === "wearable" || + root.itemType === "contentSet" || root.itemType === "avatar" || root.itemType === "app") { root.isCertified = true; } else { root.isCertified = false; @@ -311,7 +319,7 @@ Rectangle { z: 997; visible: !root.ownershipStatusReceived || !root.balanceReceived; anchors.fill: parent; - color: Qt.rgba(0.0, 0.0, 0.0, 0.7); + color: hifi.colors.white; // This object is always used in a popup. // This MouseArea is used to prevent a user from being @@ -323,8 +331,9 @@ Rectangle { } AnimatedImage { - source: "../common/images/loader.gif" - width: 96; + id: loadingImage; + source: "../common/images/loader-blue.gif" + width: 74; height: width; anchors.verticalCenter: parent.verticalCenter; anchors.horizontalCenter: parent.horizontalCenter; @@ -679,7 +688,7 @@ Rectangle { id: rezNowButton; enabled: (root.itemType === "entity" && root.canRezCertifiedItems) || (root.itemType === "contentSet" && Entities.canReplaceContent()) || - root.itemType === "wearable" || root.itemType === "avatar"; + root.itemType === "wearable" || root.itemType === "avatar" || root.itemType === "app"; buttonGlyph: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)]; color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; @@ -688,7 +697,7 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; + text: root.itemType === "app" && root.isInstalled ? "OPEN APP" : (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)]; onClicked: { if (root.itemType === "contentSet") { lightboxPopup.titleText = "Replace Content"; @@ -712,6 +721,12 @@ Rectangle { lightboxPopup.button2text = "CONFIRM"; lightboxPopup.button2method = "MyAvatar.useFullAvatarURL('" + root.itemHref + "'); root.visible = false;"; lightboxPopup.visible = true; + } else if (root.itemType === "app") { + if (root.isInstalled) { + Commerce.openApp(root.itemHref); + } else { + Commerce.installApp(root.itemHref); + } } else { sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); rezzedNotifContainer.visible = true; diff --git a/interface/resources/qml/hifi/commerce/common/images/loader-blue.gif b/interface/resources/qml/hifi/commerce/common/images/loader-blue.gif new file mode 100644 index 0000000000..8b9e17053b Binary files /dev/null and b/interface/resources/qml/hifi/commerce/common/images/loader-blue.gif differ diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index cc2bcd69aa..fb8e509cde 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -47,6 +47,7 @@ Item { property bool showConfirmation: false; property bool hasPermissionToRezThis; property bool permissionExplanationCardVisible; + property bool isInstalled; property string originalStatusText; property string originalStatusColor; @@ -62,6 +63,18 @@ Item { showConfirmation = true; } } + + onAppInstalled: { + if (appHref === root.itemHref) { + root.isInstalled = true; + } + } + + onAppUninstalled: { + if (appHref === root.itemHref) { + root.isInstalled = false; + } + } } Connections { @@ -472,6 +485,43 @@ Item { } } + Rectangle { + id: appButtonContainer; + color: hifi.colors.white; + z: 994; + visible: root.isInstalled; + anchors.fill: buttonContainer; + + HifiControlsUit.Button { + id: openAppButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.light; + anchors.top: parent.top; + anchors.right: parent.right; + anchors.left: parent.left; + width: 92; + height: 44; + text: "OPEN" + onClicked: { + Commerce.openApp(root.itemHref); + } + } + + HifiControlsUit.Button { + id: uninstallAppButton; + color: hifi.buttons.noneBorderless; + colorScheme: hifi.colorSchemes.light; + anchors.bottom: parent.bottom; + anchors.right: parent.right; + anchors.left: parent.left; + height: 44; + text: "UNINSTALL" + onClicked: { + Commerce.uninstallApp(root.itemHref); + } + } + } + Button { id: buttonContainer; property int color: hifi.buttons.blue; @@ -506,6 +556,9 @@ Item { sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref}); } else if (root.itemType === "avatar") { sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref}); + } else if (root.itemType === "app") { + // "Run" and "Uninstall" buttons are separate. + Commerce.installApp(root.itemHref); } else { sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType}); root.showConfirmation = true; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 9b333a60cd..3612de7323 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -36,6 +36,7 @@ Rectangle { property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; property int pendingItemCount: 0; + property string installedApps; // Style color: hifi.colors.white; Connections { @@ -61,6 +62,7 @@ Rectangle { root.activeView = "firstUseTutorial"; } else if (!Settings.getValue("isFirstUseOfPurchases", true) && root.activeView === "initialize") { root.activeView = "purchasesMain"; + root.installedApps = Commerce.getInstalledApps(); Commerce.inventory(); } } else { @@ -269,6 +271,7 @@ Rectangle { case 'tutorial_finished': Settings.setValue("isFirstUseOfPurchases", false); root.activeView = "purchasesMain"; + root.installedApps = Commerce.getInstalledApps(); Commerce.inventory(); break; } @@ -394,6 +397,7 @@ Rectangle { limitedRun: model.limited_run; displayedItemCount: model.displayedItemCount; permissionExplanationCardVisible: model.permissionExplanationCardVisible; + isInstalled: model.isInstalled; itemType: { if (model.root_file_url.indexOf(".fst") > -1) { "avatar"; @@ -680,9 +684,13 @@ Rectangle { if (sameItemCount !== tempPurchasesModel.count || filterBar.text !== filterBar.previousText) { filteredPurchasesModel.clear(); + var currentId; for (var i = 0; i < tempPurchasesModel.count; i++) { + currentId = tempPurchasesModel.get(i).id; + filteredPurchasesModel.append(tempPurchasesModel.get(i)); filteredPurchasesModel.setProperty(i, 'permissionExplanationCardVisible', false); + filteredPurchasesModel.setProperty(i, 'isInstalled', ((root.installedApps).indexOf(currentId) > -1)); } populateDisplayedItemCounts(); diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index fab27a29bb..bad592067c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -441,7 +441,7 @@ Item { } Item { id: choosePassphraseContainer; - visible: root.activeView === "step_3"; + visible: root.hasShownSecurityImageTip && root.activeView === "step_3"; // Anchors anchors.top: titleBarContainer.bottom; anchors.topMargin: 30; @@ -451,7 +451,10 @@ Item { onVisibleChanged: { if (visible) { + sendSignalToWallet({method: 'disableHmdPreview'}); Commerce.getWalletAuthenticatedStatus(); + } else { + sendSignalToWallet({method: 'maybeEnableHmdPreview'}); } } diff --git a/interface/resources/qml/hifi/dialogs/GraphicsPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GraphicsPreferencesDialog.qml deleted file mode 100644 index d95bafd0a9..0000000000 --- a/interface/resources/qml/hifi/dialogs/GraphicsPreferencesDialog.qml +++ /dev/null @@ -1,19 +0,0 @@ -import QtQuick 2.5 -import Qt.labs.settings 1.0 - -import "../../dialogs" - -PreferencesDialog { - id: root - objectName: "GraphicsPreferencesDialog" - title: "Graphics Settings" - showCategories: ["Graphics"] - property var settings: Settings { - category: root.objectName - property alias x: root.x - property alias y: root.y - property alias width: root.width - property alias height: root.height - } -} - diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index bbecb9e85b..19297a3251 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -207,7 +207,7 @@ Rectangle { SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND; var DYNAMIC_DEFAULT = false; var prompt = tabletRoot.customInputDialog({ textInput: { diff --git a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml deleted file mode 100644 index 25b5be05f2..0000000000 --- a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml +++ /dev/null @@ -1,37 +0,0 @@ -// -// TabletGraphicsPreferences.qml -// -// Created by Vlad Stelmahovsky on 12 Mar 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.5 -import QtQuick.Controls 1.4 -import "tabletWindows" -import "../../dialogs" - -StackView { - id: profileRoot - initialItem: root - objectName: "stack" - property string title: "Graphics Settings" - - signal sendToScript(var message); - - function pushSource(path) { - profileRoot.push(Qt.resolvedUrl(path)); - } - - function popSource() { - profileRoot.pop(); - } - - TabletPreferencesDialog { - id: root - objectName: "TabletGraphicsPreferences" - showCategories: ["Graphics"] - } -} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index efb78351ad..7f6cef6cda 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -68,7 +68,6 @@ #include #include #include -#include #include #include #include @@ -392,7 +391,7 @@ const QHash Application::_acceptedExtensi class DeadlockWatchdogThread : public QThread { public: static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1; - static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND; + static const unsigned long MAX_HEARTBEAT_AGE_USECS = 120 * USECS_PER_SECOND; // 2 mins with no checkin probably a deadlock static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples @@ -575,10 +574,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(logMessage.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#elif defined Q_OS_ANDROID +#ifdef Q_OS_ANDROID const char * local=logMessage.toStdString().c_str(); switch (type) { case QtDebugMsg: @@ -599,7 +595,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt abort(); } #endif - qApp->getLogger()->addMessage(qPrintable(logMessage + "\n")); + qApp->getLogger()->addMessage(qPrintable(logMessage)); } } @@ -785,7 +781,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(nullptr, qApp->getOcteeSceneStats()); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1515,6 +1510,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo settingsTimer->setSingleShot(false); settingsTimer->setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable QObject::connect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings); + settingsTimer->start(); }, QThread::LowestPriority); if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { @@ -1719,6 +1715,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["has_async_reprojection"] = displayPlugin->hasAsyncReprojection(); properties["hardware_stats"] = displayPlugin->getHardwareStats(); + // deadlock watchdog related stats + properties["deadlock_watchdog_maxElapsed"] = (int)DeadlockWatchdogThread::_maxElapsed; + properties["deadlock_watchdog_maxElapsedAverage"] = (int)DeadlockWatchdogThread::_maxElapsedAverage; + auto bandwidthRecorder = DependencyManager::get(); properties["packet_rate_in"] = bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(); properties["packet_rate_out"] = bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(); @@ -2606,7 +2606,6 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get().data()); - surfaceContext->setContextProperty("AvatarEntitiesBookmarks", DependencyManager::get().data()); surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get().data()); // Caches @@ -5988,7 +5987,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get()->getStats().data()); scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("AvatarEntitiesBookmarks", DependencyManager::get().data()); scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get().data()); scriptEngine->registerGlobalObject("RayPick", DependencyManager::get().data()); @@ -7543,6 +7541,18 @@ void Application::deadlockApplication() { } } +// cause main thread to be unresponsive for 35 seconds +void Application::unresponsiveApplication() { + // to avoid compiler warnings about a loop that will never exit + uint64_t start = usecTimestampNow(); + uint64_t UNRESPONSIVE_FOR_SECONDS = 35; + uint64_t UNRESPONSIVE_FOR_USECS = UNRESPONSIVE_FOR_SECONDS * USECS_PER_SECOND; + qCDebug(interfaceapp) << "Intentionally cause Interface to be unresponsive for " << UNRESPONSIVE_FOR_SECONDS << " seconds"; + while (usecTimestampNow() - start < UNRESPONSIVE_FOR_USECS) { + QThread::sleep(1); + } +} + void Application::setActiveDisplayPlugin(const QString& pluginName) { auto menu = Menu::getInstance(); foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 946cdb9764..9a902248a1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -266,9 +266,8 @@ public: float getGameLoopRate() const { return _gameLoopCounter.rate(); } - // Note that takeSnapshot has a default value, as this method is used internally. void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString()); - void takeSecondaryCameraSnapshot(const QString& filename); + void takeSecondaryCameraSnapshot(const QString& filename = QString()); void shareSnapshot(const QString& filename, const QUrl& href = QUrl("")); @@ -369,6 +368,7 @@ public slots: void updateHeartbeat() const; static void deadlockApplication(); + static void unresponsiveApplication(); // cause main thread to be unresponsive for 35 seconds void rotationModeChanged() const; diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 0f177e3d15..7845158a80 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -19,6 +19,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "MainWindow.h" #include "Menu.h" @@ -29,6 +34,62 @@ #include + +void addAvatarEntities(const QVariantList& avatarEntities) { + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + EntityTreePointer entityTree = DependencyManager::get()->getTree(); + if (!entityTree) { + return; + } + EntitySimulationPointer entitySimulation = entityTree->getSimulation(); + PhysicalEntitySimulationPointer physicalEntitySimulation = std::static_pointer_cast(entitySimulation); + EntityEditPacketSender* entityPacketSender = physicalEntitySimulation->getPacketSender(); + QScriptEngine scriptEngine; + for (int index = 0; index < avatarEntities.count(); index++) { + const QVariantMap& avatarEntityProperties = avatarEntities.at(index).toMap(); + QVariant variantProperties = avatarEntityProperties["properties"]; + QVariantMap asMap = variantProperties.toMap(); + QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); + EntityItemProperties entityProperties; + EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties); + + entityProperties.setParentID(myNodeID); + entityProperties.setClientOnly(true); + entityProperties.setOwningAvatarID(myNodeID); + entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY); + entityProperties.markAllChanged(); + + EntityItemID id = EntityItemID(QUuid::createUuid()); + bool success = true; + entityTree->withWriteLock([&] { + EntityItemPointer entity = entityTree->addEntity(id, entityProperties); + if (entity) { + if (entityProperties.queryAACubeRelatedPropertyChanged()) { + // due to parenting, the server may not know where something is in world-space, so include the bounding cube. + bool success; + AACube queryAACube = entity->getQueryAACube(success); + if (success) { + entityProperties.setQueryAACube(queryAACube); + } + } + + entity->setLastBroadcast(usecTimestampNow()); + // since we're creating this object we will immediately volunteer to own its simulation + entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY); + entityProperties.setLastEdited(entity->getLastEdited()); + } else { + qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree"; + success = false; + } + }); + + if (success) { + entityPacketSender->queueEditEntityMessage(PacketType::EntityAdd, entityTree, id, entityProperties); + } + } +} + AvatarBookmarks::AvatarBookmarks() { _bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATARBOOKMARKS_FILENAME; readFromFile(); @@ -38,7 +99,7 @@ void AvatarBookmarks::readFromFile() { // migrate old avatarbookmarks.json, used to be in 'local' folder on windows QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME; QFile oldConfig(oldConfigPath); - + // I imagine that in a year from now, this code for migrating (as well as the two lines above) // may be removed since all bookmarks should have been migrated by then // - Robbie Uvanni (6.8.2017) @@ -48,9 +109,9 @@ void AvatarBookmarks::readFromFile() { } else { qCDebug(interfaceapp) << "Failed to migrate" << AVATARBOOKMARKS_FILENAME; } - } - - Bookmarks::readFromFile(); + } + + Bookmarks::readFromFile(); } void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { @@ -81,23 +142,27 @@ void AvatarBookmarks::changeToBookmarkedAvatar() { myAvatar->useFullAvatarURL(action->data().toString()); qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark "; } else { - + const QMap bookmark = action->data().toMap(); - // Not magic value. This is the current made version, and if it changes this interpreter should be updated to + // Not magic value. This is the current made version, and if it changes this interpreter should be updated to // handle the new one separately. // This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous if (bookmark.value(ENTRY_VERSION) == 3) { - const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); - myAvatar->useFullAvatarURL(avatarUrl); - qCDebug(interfaceapp) << "Avatar On " << avatarUrl; - const QList& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList()).toList(); + myAvatar->removeAvatarEntities(); + const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); + myAvatar->useFullAvatarURL(avatarUrl); + qCDebug(interfaceapp) << "Avatar On " << avatarUrl; + const QList& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList()).toList(); - qCDebug(interfaceapp) << "Attach " << attachments; - myAvatar->setAttachmentsVariant(attachments); + qCDebug(interfaceapp) << "Attach " << attachments; + myAvatar->setAttachmentsVariant(attachments); + + const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); + myAvatar->setAvatarScale(qScale); + + const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList(); + addAvatarEntities(avatarEntities); - const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); - myAvatar->setAvatarScale(qScale); - } else { qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark"; } @@ -126,6 +191,7 @@ void AvatarBookmarks::addBookmark() { bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale); bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); + bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant()); Bookmarks::addBookmarkToFile(bookmarkName, bookmark); }); diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index 0529eeb516..7e2f64379e 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -34,6 +34,7 @@ private: const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; const QString ENTRY_AVATAR_URL = "avatarUrl"; const QString ENTRY_AVATAR_ATTACHMENTS = "attachments"; + const QString ENTRY_AVATAR_ENTITIES = "avatarEntites"; const QString ENTRY_AVATAR_SCALE = "avatarScale"; const QString ENTRY_VERSION = "version"; diff --git a/interface/src/AvatarEntitiesBookmarks.cpp b/interface/src/AvatarEntitiesBookmarks.cpp deleted file mode 100644 index 21d3657346..0000000000 --- a/interface/src/AvatarEntitiesBookmarks.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// -// AvatarEntitiesBookmarks.cpp -// interface/src -// -// Created by Dante Ruiz on 15/01/18. -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "MainWindow.h" -#include "Menu.h" -#include "AvatarEntitiesBookmarks.h" -#include "InterfaceLogging.h" - -#include "QVariantGLM.h" - -#include - -void addAvatarEntities(const QVariantList& avatarEntities) { - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); - EntityTreePointer entityTree = DependencyManager::get()->getTree(); - if (!entityTree) { - return; - } - EntitySimulationPointer entitySimulation = entityTree->getSimulation(); - PhysicalEntitySimulationPointer physicalEntitySimulation = std::static_pointer_cast(entitySimulation); - EntityEditPacketSender* entityPacketSender = physicalEntitySimulation->getPacketSender(); - QScriptEngine scriptEngine; - for (int index = 0; index < avatarEntities.count(); index++) { - const QVariantMap& avatarEntityProperties = avatarEntities.at(index).toMap(); - QVariant variantProperties = avatarEntityProperties["properties"]; - QVariantMap asMap = variantProperties.toMap(); - QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); - EntityItemProperties entityProperties; - EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties); - - entityProperties.setParentID(myNodeID); - entityProperties.setClientOnly(true); - entityProperties.setOwningAvatarID(myNodeID); - entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY); - entityProperties.markAllChanged(); - - EntityItemID id = EntityItemID(QUuid::createUuid()); - bool success = true; - entityTree->withWriteLock([&] { - EntityItemPointer entity = entityTree->addEntity(id, entityProperties); - if (entity) { - if (entityProperties.queryAACubeRelatedPropertyChanged()) { - // due to parenting, the server may not know where something is in world-space, so include the bounding cube. - bool success; - AACube queryAACube = entity->getQueryAACube(success); - if (success) { - entityProperties.setQueryAACube(queryAACube); - } - } - - entity->setLastBroadcast(usecTimestampNow()); - // since we're creating this object we will immediately volunteer to own its simulation - entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY); - entityProperties.setLastEdited(entity->getLastEdited()); - } else { - qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree"; - success = false; - } - }); - - if (success) { - entityPacketSender->queueEditEntityMessage(PacketType::EntityAdd, entityTree, id, entityProperties); - } - } -} - -AvatarEntitiesBookmarks::AvatarEntitiesBookmarks() { - _bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATAR_ENTITIES_BOOKMARKS_FILENAME; - Bookmarks::readFromFile(); -} - -void AvatarEntitiesBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { - auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatarEntities); - QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection); - _bookmarksMenu = menu->addMenu(MenuOption::AvatarEntitiesBookmarks); - _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarEntitiesBookmark); - QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection); - - for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) { - addBookmarkToMenu(menubar, it.key(), it.value()); - } - - Bookmarks::sortActions(menubar, _bookmarksMenu); -} - -void AvatarEntitiesBookmarks::applyBookmarkedAvatarEntities() { - QAction* action = qobject_cast(sender()); - auto myAvatar = DependencyManager::get()->getMyAvatar(); - - const QMap bookmark = action->data().toMap(); - - if (bookmark.value(ENTRY_VERSION) == AVATAR_ENTITIES_BOOKMARK_VERSION) { - myAvatar->removeAvatarEntities(); - const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); - myAvatar->useFullAvatarURL(avatarUrl); - const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList(); - addAvatarEntities(avatarEntities); - const float& avatarScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); - myAvatar->setAvatarScale(avatarScale); - } else { - qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarEntitiesBookmark"; - } -} - -void AvatarEntitiesBookmarks::addBookmark() { - ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar Entities", "Name", QString()); - connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) { - disconnect(dlg, &ModalDialogListener::response, this, nullptr); - auto bookmarkName = response.toString(); - bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); - if (bookmarkName.length() == 0) { - return; - } - - auto myAvatar = DependencyManager::get()->getMyAvatar(); - - const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString(); - const QVariant& avatarScale = myAvatar->getAvatarScale(); - - QVariantMap bookmark; - bookmark.insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION); - bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); - bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale); - bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant()); - - Bookmarks::addBookmarkToFile(bookmarkName, bookmark); - }); -} - -void AvatarEntitiesBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) { - QAction* changeAction = _bookmarksMenu->newAction(); - changeAction->setData(bookmark); - connect(changeAction, SIGNAL(triggered()), this, SLOT(applyBookmarkedAvatarEntities())); - if (!_isMenuSorted) { - menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole); - } else { - // TODO: this is aggressive but other alternatives have proved less fruitful so far. - menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole); - Bookmarks::sortActions(menubar, _bookmarksMenu); - } -} diff --git a/interface/src/AvatarEntitiesBookmarks.h b/interface/src/AvatarEntitiesBookmarks.h deleted file mode 100644 index 0c70e4dbc0..0000000000 --- a/interface/src/AvatarEntitiesBookmarks.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// AvatarEntitiesBookmarks.h -// interface/src -// -// Created by Dante Ruiz on 15/01/18. -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AvatarEntitiesBookmarks_h -#define hifi_AvatarEntitiesBookmarks_h - -#include -#include "Bookmarks.h" - -class AvatarEntitiesBookmarks: public Bookmarks, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: - AvatarEntitiesBookmarks(); - void setupMenus(Menu* menubar, MenuWrapper* menu) override; - -public slots: - void addBookmark(); - -protected: - void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override; - -private: - const QString AVATAR_ENTITIES_BOOKMARKS_FILENAME = "AvatarEntitiesBookmarks.json"; - const QString ENTRY_AVATAR_URL = "AvatarUrl"; - const QString ENTRY_AVATAR_SCALE = "AvatarScale"; - const QString ENTRY_AVATAR_ENTITIES = "AvatarEntities"; - const QString ENTRY_VERSION = "version"; - - const int AVATAR_ENTITIES_BOOKMARK_VERSION = 1; - -private slots: - void applyBookmarkedAvatarEntities(); -}; - -#endif diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 464de87fdb..17b670a343 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -34,7 +34,6 @@ #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" #include "AvatarBookmarks.h" -#include "AvatarEntitiesBookmarks.h" #include "devices/DdeFaceTracker.h" #include "MainWindow.h" #include "render/DrawStatus.h" @@ -44,6 +43,7 @@ #include "ui/StandAloneJSConsole.h" #include "InterfaceLogging.h" #include "LocationBookmarks.h" +#include "DeferredLightingEffect.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" @@ -207,10 +207,6 @@ Menu::Menu() { auto avatarBookmarks = DependencyManager::get(); avatarBookmarks->setupMenus(this, avatarMenu); - auto avatarEntitiesBookmarks = DependencyManager::get(); - avatarEntitiesBookmarks->setupMenus(this, avatarMenu); - - // Display menu ---------------------------------- // FIXME - this is not yet matching Alan's spec because it doesn't have // menus for "2D"/"3D" - we need to add support for detecting the appropriate @@ -366,10 +362,15 @@ Menu::Menu() { MenuWrapper* developerMenu = addMenu("Developer", "Developer"); // Developer > Graphics... - action = addActionToQMenuAndActionHash(developerMenu, "Graphics..."); - connect(action, &QAction::triggered, [] { - qApp->showDialog(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"), - QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog"); + MenuWrapper* graphicsOptionsMenu = developerMenu->addMenu("Render"); + action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::Shadows, 0, true); + connect(action, &QAction::triggered, [action] { + DependencyManager::get()->setShadowMapEnabled(action->isChecked()); + }); + + action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::AmbientOcclusion, 0, false); + connect(action, &QAction::triggered, [action] { + DependencyManager::get()->setAmbientOcclusionEnabled(action->isChecked()); }); // Developer > UI >>> @@ -713,6 +714,7 @@ Menu::Menu() { MenuWrapper* crashMenu = developerMenu->addMenu("Crash"); addActionToQMenuAndActionHash(crashMenu, MenuOption::DeadlockInterface, 0, qApp, SLOT(deadlockApplication())); + addActionToQMenuAndActionHash(crashMenu, MenuOption::UnresponsiveInterface, 0, qApp, SLOT(unresponsiveApplication())); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashPureVirtualFunction); connect(action, &QAction::triggered, qApp, []() { crash::pureVirtualCall(); }); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 8cb1804fd4..1d37b74ffe 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -46,7 +46,6 @@ namespace MenuOption { const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AvatarReceiveStats = "Show Receive Stats"; const QString AvatarBookmarks = "Avatar Bookmarks"; - const QString AvatarEntitiesBookmarks = "Avatar Entities Bookmarks"; const QString Back = "Back"; const QString BinaryEyelidControl = "Binary Eyelid Control"; const QString BookmarkAvatar = "Bookmark Avatar"; @@ -77,6 +76,7 @@ namespace MenuOption { const QString CrashNewFault = "New Fault"; const QString CrashNewFaultThreaded = "New Fault (threaded)"; const QString DeadlockInterface = "Deadlock Interface"; + const QString UnresponsiveInterface = "Unresponsive Interface"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DefaultSkybox = "Default Skybox"; const QString DeleteAvatarBookmark = "Delete Avatar Bookmark..."; @@ -204,6 +204,8 @@ namespace MenuOption { const QString WorldAxes = "World Axes"; const QString DesktopTabletToToolbar = "Desktop Tablet Becomes Toolbar"; const QString HMDTabletToToolbar = "HMD Tablet Becomes Toolbar"; + const QString Shadows = "Shadows"; + const QString AmbientOcclusion = "AmbientOcclusion"; } #endif // hifi_Menu_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 50753724e8..2ada31e1ed 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -79,6 +79,8 @@ float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f; const int SCRIPTED_MOTOR_CAMERA_FRAME = 0; const int SCRIPTED_MOTOR_AVATAR_FRAME = 1; const int SCRIPTED_MOTOR_WORLD_FRAME = 2; +const int SCRIPTED_MOTOR_SIMPLE_MODE = 0; +const int SCRIPTED_MOTOR_DYNAMIC_MODE = 1; const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav"; const float MyAvatar::ZOOM_MIN = 0.5f; @@ -92,6 +94,7 @@ MyAvatar::MyAvatar(QThread* thread) : _pitchSpeed(PITCH_SPEED_DEFAULT), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), + _scriptedMotorMode(SCRIPTED_MOTOR_SIMPLE_MODE), _motionBehaviors(AVATAR_MOTION_DEFAULTS), _characterController(this), _eyeContactTarget(LEFT_EYE), @@ -1112,6 +1115,7 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) { void MyAvatar::setEnableMeshVisible(bool isEnabled) { _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); + _skeletonModel->setCanCastShadow(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { @@ -1464,6 +1468,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { int skeletonModelChangeCount = _skeletonModelChangeCount; Avatar::setSkeletonModelURL(skeletonModelURL); _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); + _skeletonModel->setCanCastShadow(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); _headBoneSet.clear(); _cauterizationNeedsUpdate = true; @@ -1623,20 +1628,27 @@ controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action act void MyAvatar::updateMotors() { _characterController.clearMotors(); glm::quat motorRotation; + + const float FLYING_MOTOR_TIMESCALE = 0.05f; + const float WALKING_MOTOR_TIMESCALE = 0.2f; + const float INVALID_MOTOR_TIMESCALE = 1.0e6f; + + float horizontalMotorTimescale; + float verticalMotorTimescale; + + if (_characterController.getState() == CharacterController::State::Hover || + _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { + horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE; + verticalMotorTimescale = FLYING_MOTOR_TIMESCALE; + } else { + horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE; + verticalMotorTimescale = INVALID_MOTOR_TIMESCALE; + } + if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { - - const float FLYING_MOTOR_TIMESCALE = 0.05f; - const float WALKING_MOTOR_TIMESCALE = 0.2f; - const float INVALID_MOTOR_TIMESCALE = 1.0e6f; - - float horizontalMotorTimescale; - float verticalMotorTimescale; - if (_characterController.getState() == CharacterController::State::Hover || _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { motorRotation = getMyHead()->getHeadOrientation(); - horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE; - verticalMotorTimescale = FLYING_MOTOR_TIMESCALE; } else { // non-hovering = walking: follow camera twist about vertical but not lift // we decompose camera's rotation and store the twist part in motorRotation @@ -1647,8 +1659,6 @@ void MyAvatar::updateMotors() { glm::quat liftRotation; swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation); motorRotation = orientation * motorRotation; - horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE; - verticalMotorTimescale = INVALID_MOTOR_TIMESCALE; } if (_isPushing || _isBraking || !_isBeingPushed) { @@ -1668,7 +1678,12 @@ void MyAvatar::updateMotors() { // world-frame motorRotation = glm::quat(); } - _characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale); + if (_scriptedMotorMode == SCRIPTED_MOTOR_SIMPLE_MODE) { + _characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale); + } else { + // dynamic mode + _characterController.addMotor(_scriptedMotorVelocity, motorRotation, horizontalMotorTimescale, verticalMotorTimescale); + } } // legacy support for 'MyAvatar::applyThrust()', which has always been implemented as a @@ -1752,6 +1767,14 @@ QString MyAvatar::getScriptedMotorFrame() const { return frame; } +QString MyAvatar::getScriptedMotorMode() const { + QString mode = "simple"; + if (_scriptedMotorMode == SCRIPTED_MOTOR_DYNAMIC_MODE) { + mode = "dynamic"; + } + return mode; +} + void MyAvatar::setScriptedMotorVelocity(const glm::vec3& velocity) { float MAX_SCRIPTED_MOTOR_SPEED = 500.0f; _scriptedMotorVelocity = velocity; @@ -1778,6 +1801,14 @@ void MyAvatar::setScriptedMotorFrame(QString frame) { } } +void MyAvatar::setScriptedMotorMode(QString mode) { + if (mode.toLower() == "simple") { + _scriptedMotorMode = SCRIPTED_MOTOR_SIMPLE_MODE; + } else if (mode.toLower() == "dynamic") { + _scriptedMotorMode = SCRIPTED_MOTOR_DYNAMIC_MODE; + } +} + void MyAvatar::clearScriptableSettings() { _scriptedMotorVelocity = Vectors::ZERO; _scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE; @@ -1819,12 +1850,6 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved); } -void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) { - if (model->isActive() && model->isRenderable()) { - model->setVisibleInScene(visible, scene, render::ItemKey::TAG_BITS_NONE, true); - } -} - void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { @@ -2014,8 +2039,11 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { _attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { + _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); + + _attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); } } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 28af8b62fd..2615f8fa0f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -69,6 +69,7 @@ class MyAvatar : public Avatar { * @property motorTimescale {float} Specifies how quickly the avatar should accelerate to meet the motorVelocity, * smaller values will result in higher acceleration. * @property motorReferenceFrame {string} Reference frame of the motorVelocity, must be one of the following: "avatar", "camera", "world" + * @property motorMode {string} Type of scripted motor behavior, "simple" = use motorTimescale property (default mode) and "dynamic" = use action motor's timescales * @property collisionSoundURL {string} Specifies the sound to play when the avatar experiences a collision. * You can provide a mono or stereo 16-bit WAV file running at either 24 Khz or 48 Khz. * The latter is downsampled by the audio mixer, so all audio effectively plays back at a 24 Khz sample rate. @@ -124,6 +125,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame) + Q_PROPERTY(QString motorMode READ getScriptedMotorMode WRITE setScriptedMotorMode) Q_PROPERTY(QString collisionSoundURL READ getCollisionSoundURL WRITE setCollisionSoundURL) Q_PROPERTY(AudioListenerMode audioListenerMode READ getAudioListenerMode WRITE setAudioListenerMode) Q_PROPERTY(glm::vec3 customListenPosition READ getCustomListenPosition WRITE setCustomListenPosition) @@ -662,9 +664,11 @@ private: glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; } QString getScriptedMotorFrame() const; + QString getScriptedMotorMode() const; void setScriptedMotorVelocity(const glm::vec3& velocity); void setScriptedMotorTimescale(float timescale); void setScriptedMotorFrame(QString frame); + void setScriptedMotorMode(QString mode); virtual void attach(const QString& modelURL, const QString& jointName = QString(), const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, bool isSoft = false, @@ -677,8 +681,6 @@ private: // These are made private for MyAvatar so that you will use the "use" methods instead virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; - void setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visiblity); - virtual void updatePalms() override {} void lateUpdatePalms(); @@ -706,6 +708,7 @@ private: glm::vec3 _scriptedMotorVelocity; // target local-frame velocity of avatar (analog script) float _scriptedMotorTimescale; // timescale for avatar to achieve its target velocity int _scriptedMotorFrame; + int _scriptedMotorMode; quint32 _motionBehaviors; QString _collisionSoundURL; diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 36c1e422c5..e7d62930cf 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -10,6 +10,7 @@ // #include "QmlCommerce.h" +#include "CommerceLogging.h" #include "Application.h" #include "DependencyManager.h" #include "Ledger.h" @@ -17,6 +18,9 @@ #include #include #include +#include +#include +#include "scripting/HMDScriptingInterface.h" QmlCommerce::QmlCommerce() { auto ledger = DependencyManager::get(); @@ -40,6 +44,8 @@ QmlCommerce::QmlCommerce() { connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { setPassphrase(""); }); + + _appsPath = PathUtils::getAppDataPath() + "Apps/"; } void QmlCommerce::getWalletStatus() { @@ -183,3 +189,148 @@ void QmlCommerce::alreadyOwned(const QString& marketplaceId) { auto ledger = DependencyManager::get(); ledger->alreadyOwned(marketplaceId); } + +QString QmlCommerce::getInstalledApps() { + QString installedAppsFromMarketplace; + QStringList runningScripts = DependencyManager::get()->getRunningScripts(); + + QDir directory(_appsPath); + QStringList apps = directory.entryList(QStringList("*.app.json")); + foreach(QString appFileName, apps) { + installedAppsFromMarketplace += appFileName; + installedAppsFromMarketplace += ","; + QFile appFile(_appsPath + appFileName); + if (appFile.open(QIODevice::ReadOnly)) { + QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll()); + + appFile.close(); + + QJsonObject appFileJsonObject = appFileJsonDocument.object(); + QString scriptURL = appFileJsonObject["scriptURL"].toString(); + + // If the script .app.json is on the user's local disk but the associated script isn't running + // for some reason, start that script again. + if (!runningScripts.contains(scriptURL)) { + if ((DependencyManager::get()->loadScript(scriptURL.trimmed())).isNull()) { + qCDebug(commerce) << "Couldn't start script while checking installed apps."; + } + } + } else { + qCDebug(commerce) << "Couldn't open local .app.json file for reading."; + } + } + + return installedAppsFromMarketplace; +} + +bool QmlCommerce::installApp(const QString& itemHref) { + if (!QDir(_appsPath).exists()) { + if (!QDir().mkdir(_appsPath)) { + qCDebug(commerce) << "Couldn't make _appsPath directory."; + return false; + } + } + + QUrl appHref(itemHref); + + auto request = DependencyManager::get()->createResourceRequest(this, appHref); + + if (!request) { + qCDebug(commerce) << "Couldn't create resource request for app."; + return false; + } + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() != ResourceRequest::Success) { + qCDebug(commerce) << "Failed to get .app.json file from remote."; + return false; + } + + // Copy the .app.json to the apps directory inside %AppData%/High Fidelity/Interface + auto requestData = request->getData(); + QFile appFile(_appsPath + "/" + appHref.fileName()); + if (!appFile.open(QIODevice::WriteOnly)) { + qCDebug(commerce) << "Couldn't open local .app.json file for creation."; + return false; + } + if (appFile.write(requestData) == -1) { + qCDebug(commerce) << "Couldn't write to local .app.json file."; + return false; + } + // Close the file + appFile.close(); + + // Read from the returned datastream to know what .js to add to Running Scripts + QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(requestData); + QJsonObject appFileJsonObject = appFileJsonDocument.object(); + QString scriptUrl = appFileJsonObject["scriptURL"].toString(); + + if ((DependencyManager::get()->loadScript(scriptUrl.trimmed())).isNull()) { + qCDebug(commerce) << "Couldn't load script."; + return false; + } + + emit appInstalled(itemHref); + return true; + }); + request->send(); + return true; +} + +bool QmlCommerce::uninstallApp(const QString& itemHref) { + QUrl appHref(itemHref); + + // Read from the file to know what .js script to stop + QFile appFile(_appsPath + "/" + appHref.fileName()); + if (!appFile.open(QIODevice::ReadOnly)) { + qCDebug(commerce) << "Couldn't open local .app.json file for deletion."; + return false; + } + QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll()); + QJsonObject appFileJsonObject = appFileJsonDocument.object(); + QString scriptUrl = appFileJsonObject["scriptURL"].toString(); + + if (!DependencyManager::get()->stopScript(scriptUrl.trimmed(), false)) { + qCDebug(commerce) << "Couldn't stop script."; + return false; + } + + // Delete the .app.json from the filesystem + // remove() closes the file first. + if (!appFile.remove()) { + qCDebug(commerce) << "Couldn't delete local .app.json file."; + return false; + } + + emit appUninstalled(itemHref); + return true; +} + +bool QmlCommerce::openApp(const QString& itemHref) { + QUrl appHref(itemHref); + + // Read from the file to know what .html or .qml document to open + QFile appFile(_appsPath + "/" + appHref.fileName()); + if (!appFile.open(QIODevice::ReadOnly)) { + qCDebug(commerce) << "Couldn't open local .app.json file."; + return false; + } + QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll()); + QJsonObject appFileJsonObject = appFileJsonDocument.object(); + QString homeUrl = appFileJsonObject["homeURL"].toString(); + + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + if (homeUrl.contains(".qml", Qt::CaseInsensitive)) { + tablet->loadQMLSource(homeUrl); + } else if (homeUrl.contains(".html", Qt::CaseInsensitive)) { + tablet->gotoWebScreen(homeUrl); + } else { + qCDebug(commerce) << "Attempted to open unknown type of homeURL!"; + return false; + } + + DependencyManager::get()->openTablet(); + + return true; +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index b621608190..09eb7137af 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -51,6 +51,9 @@ signals: void contentSetChanged(const QString& contentSetHref); + void appInstalled(const QString& appHref); + void appUninstalled(const QString& appHref); + protected: Q_INVOKABLE void getWalletStatus(); @@ -76,8 +79,15 @@ protected: Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage); - Q_INVOKABLE void replaceContentSet(const QString& itemHref); + + Q_INVOKABLE QString getInstalledApps(); + Q_INVOKABLE bool installApp(const QString& appHref); + Q_INVOKABLE bool uninstallApp(const QString& appHref); + Q_INVOKABLE bool openApp(const QString& appHref); + +private: + QString _appsPath; }; #endif // hifi_QmlCommerce_h diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 30e8439985..e80dc1d213 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -38,6 +38,7 @@ extern "C" { #endif int main(int argc, const char* argv[]) { + setupHifiApplication(BuildInfo::INTERFACE_NAME); #ifdef Q_OS_LINUX QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); @@ -51,17 +52,9 @@ int main(int argc, const char* argv[]) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); #endif - disableQtBearerPoll(); // Fixes wifi ping spikes - QElapsedTimer startupTime; startupTime.start(); - // Set application infos - QCoreApplication::setApplicationName(BuildInfo::INTERFACE_NAME); - QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); - QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); - QCoreApplication::setApplicationVersion(BuildInfo::VERSION); - Setting::init(); // Instance UserActivityLogger now that the settings are loaded diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index c792834d9c..122b58c057 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -17,6 +17,8 @@ #include "SceneScriptingInterface.h" OctreePacketProcessor::OctreePacketProcessor() { + setObjectName("Octree Packet Processor"); + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 8d0c5c246b..67fdea0bc5 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -305,6 +305,7 @@ public slots: * {@link Window.stillSnapshotTaken|stillSnapshotTaken} is emitted; when a still image plus moving images are captured, * {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted} * are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings > + * NOTE: to provide a non-default value - all previous parameters must be provided. * General > Snapshots. * @function Window.takeSnapshot * @param {boolean} notify=true - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} @@ -314,8 +315,10 @@ public slots: * @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is 0 the * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the * dimensions is adjusted in order to match the aspect ratio. - * @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ",jpg". - * otherwise, the image will be saved as 'hifi-snap-by--YYYY-MM-DD_HH-MM-SS' + * @param {string} filename="" - If this parameter is not given, the image will be saved as 'hifi-snap-by--YYYY-MM-DD_HH-MM-SS'. + * If this parameter is "" then the image will be saved as ".jpg". + * Otherwise, the image will be saved to this filename, with an appended ".jpg". + * * @example Using the snapshot function and signals. * function onStillSnapshotTaken(path, notify) { * print("Still snapshot taken: " + path); @@ -337,15 +340,18 @@ public slots: * var notify = true; * var animated = true; * var aspect = 1920 / 1080; - * var filename = QString(); + * var filename = ""; * Window.takeSnapshot(notify, animated, aspect, filename); */ void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString()); /**jsdoc * Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API. + * NOTE: to provide a non-default value - all previous parameters must be provided. * @function Window.takeSecondaryCameraSnapshot - * @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ".jpg" + * @param {string} filename="" - If this parameter is not given, the image will be saved as 'hifi-snap-by--YYYY-MM-DD_HH-MM-SS'. + * If this parameter is "" then the image will be saved as ".jpg". + * Otherwise, the image will be saved to this filename, with an appended ".jpg". * * var filename = QString(); */ diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 1112ccde60..b2a494230b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -31,6 +31,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : _defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)), _headClipDistance(DEFAULT_NEAR_CLIP) { + // SkeletonModels, and by extention Avatars, use Dual Quaternion skinning. + _useDualQuaternionSkinning = true; assert(_owningAvatar); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index d3c9f3d4bd..c33b87e5cf 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -164,7 +164,12 @@ ItemKey EntityRenderer::getKey() { return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } - return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); + // This allows shapes to cast shadows + if (_canCastShadow) { + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).withShadowCaster(); + } else { + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); + } } uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { @@ -377,6 +382,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa _moving = entity->isMovingRelativeToParent(); _visible = entity->getVisible(); + _canCastShadow = entity->getCanCastShadow(); _cauterized = entity->getCauterized(); _needsRenderUpdate = false; }); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index ce652a758c..823debf02b 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -126,6 +126,7 @@ protected: bool _isFading{ _entitiesShouldFadeFunction() }; bool _prevIsTransparent { false }; bool _visible { false }; + bool _canCastShadow { false }; bool _cauterized { false }; bool _moving { false }; bool _needsRenderUpdate { false }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 83ec675adf..f759198e72 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -133,6 +133,9 @@ void RenderableModelEntityItem::doInitialModelSimulation() { model->setRotation(getWorldOrientation()); model->setTranslation(getWorldPosition()); + glm::vec3 scale = model->getScale(); + model->setUseDualQuaternionSkinning(!isNonUniformScale(scale)); + if (_needsInitialSimulation) { model->simulate(0.0f); _needsInitialSimulation = false; @@ -243,6 +246,8 @@ void RenderableModelEntityItem::updateModelBounds() { } if (updateRenderItems) { + glm::vec3 scale = model->getScale(); + model->setUseDualQuaternionSkinning(!isNonUniformScale(scale)); model->updateRenderItems(); } } @@ -1356,6 +1361,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } // TODO? early exit here when not visible? + if (model->canCastShadow() != _canCastShadow) { + model->setCanCastShadow(_canCastShadow, scene, viewTaskBits, false); + } + if (_needsCollisionGeometryUpdate) { setCollisionMeshKey(entity->getCollisionMeshKey()); _needsCollisionGeometryUpdate = false; @@ -1500,4 +1509,4 @@ void ModelEntityRenderer::processMaterials() { material.pop(); } } -} \ No newline at end of file +} diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 34703804cd..322c91e3d3 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -330,6 +330,7 @@ void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity sunLight->setColor(ColorUtils::toVec3(_keyLightProperties.getColor())); sunLight->setIntensity(_keyLightProperties.getIntensity()); sunLight->setDirection(entity->getTransform().getRotation() * _keyLightProperties.getDirection()); + sunLight->setCastShadows(_keyLightProperties.getCastShadows()); } void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& entity) { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ec0bdbd2ae..f77d8a59c3 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -91,6 +91,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_REGISTRATION_POINT; requestedProperties += PROP_ANGULAR_DAMPING; requestedProperties += PROP_VISIBLE; + requestedProperties += PROP_CAN_CAST_SHADOW; requestedProperties += PROP_COLLISIONLESS; requestedProperties += PROP_COLLISION_MASK; requestedProperties += PROP_DYNAMIC; @@ -249,6 +250,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping()); APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible()); + APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, getCanCastShadow()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONLESS, getCollisionless()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, getCollisionMask()); APPEND_ENTITY_PROPERTY(PROP_DYNAMIC, getDynamic()); @@ -799,6 +801,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, setAngularDamping); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible); + READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, setCollisionless); READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, setCollisionMask); READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, setDynamic); @@ -1234,6 +1237,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping); COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRenderAlpha, getLocalRenderAlpha); COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(canCastShadow, getCanCastShadow); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionless, getCollisionless); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionMask, getCollisionMask); COPY_ENTITY_PROPERTY_TO_PROPERTIES(dynamic, getDynamic); @@ -1346,6 +1350,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(canCastShadow, setCanCastShadow); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); // Certifiable Properties @@ -2723,6 +2728,28 @@ void EntityItem::setVisible(bool value) { } } +bool EntityItem::getCanCastShadow() const { + bool result; + withReadLock([&] { + result = _canCastShadow; + }); + return result; +} + +void EntityItem::setCanCastShadow(bool value) { + bool changed = false; + withWriteLock([&] { + if (_canCastShadow != value) { + changed = true; + _canCastShadow = value; + } + }); + + if (changed) { + emit requestRenderUpdate(); + } +} + bool EntityItem::isChildOfMyAvatar() const { QUuid ancestorID = findAncestorOfType(NestableType::Avatar); return !ancestorID.isNull() && (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b12417c496..d08c5514e9 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -274,6 +274,10 @@ public: bool getVisible() const; void setVisible(bool value); + + bool getCanCastShadow() const; + void setCanCastShadow(bool value); + inline bool isVisible() const { return getVisible(); } inline bool isInvisible() const { return !getVisible(); } @@ -551,6 +555,7 @@ protected: glm::vec3 _registrationPoint { ENTITY_ITEM_DEFAULT_REGISTRATION_POINT }; float _angularDamping { ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING }; bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE }; + bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW }; bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS }; uint8_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT }; bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a2724c4cbf..2eca612fc2 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -316,6 +316,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible); + CHECK_PROPERTY_CHANGE(PROP_CAN_CAST_SHADOW, canCastShadow); CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint); CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity); CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping); @@ -490,6 +491,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_VELOCITY, angularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE, visible); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CAN_CAST_SHADOW, canCastShadow); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISIONLESS, collisionless); COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_COLLISIONLESS, collisionless, ignoreForCollisions, getCollisionless()); // legacy support COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_MASK, collisionMask); @@ -751,6 +753,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(angularVelocity, glmVec3, setAngularVelocity); COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping); COPY_PROPERTY_FROM_QSCRIPTVALUE(visible, bool, setVisible); + COPY_PROPERTY_FROM_QSCRIPTVALUE(canCastShadow, bool, setCanCastShadow); COPY_PROPERTY_FROM_QSCRIPTVALUE(color, xColor, setColor); COPY_PROPERTY_FROM_QSCRIPTVALUE(colorSpread, xColor, setColorSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, xColor, setColorStart); @@ -922,6 +925,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(angularVelocity); COPY_PROPERTY_IF_CHANGED(angularDamping); COPY_PROPERTY_IF_CHANGED(visible); + COPY_PROPERTY_IF_CHANGED(canCastShadow); COPY_PROPERTY_IF_CHANGED(color); COPY_PROPERTY_IF_CHANGED(colorSpread); COPY_PROPERTY_IF_CHANGED(colorStart); @@ -1094,6 +1098,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue std::call_once(initMap, [](){ ADD_PROPERTY_TO_MAP(PROP_VISIBLE, Visible, visible, bool); + ADD_PROPERTY_TO_MAP(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool); ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, glm::vec3); ADD_PROPERTY_TO_MAP(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3); ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, glm::quat); @@ -1187,6 +1192,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_CAST_SHADOW, KeyLightCastShadows, keyLightCastShadows, bool); + ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3); ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray); ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); @@ -1412,6 +1419,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, properties.getAngularDamping()); APPEND_ENTITY_PROPERTY(PROP_VISIBLE, properties.getVisible()); + APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, properties.getCanCastShadow()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONLESS, properties.getCollisionless()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, properties.getCollisionMask()); APPEND_ENTITY_PROPERTY(PROP_DYNAMIC, properties.getDynamic()); @@ -1784,6 +1792,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONLESS, bool, setCollisionless); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint8_t, setCollisionMask); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DYNAMIC, bool, setDynamic); @@ -2053,6 +2062,7 @@ void EntityItemProperties::markAllChanged() { _angularDampingChanged = true; _nameChanged = true; _visibleChanged = true; + _canCastShadowChanged = true; _colorChanged = true; _alphaChanged = true; _modelURLChanged = true; @@ -2250,6 +2260,9 @@ QList EntityItemProperties::listChangedProperties() { if (visibleChanged()) { out += "visible"; } + if (canCastShadowChanged()) { + out += "canCastShadow"; + } if (rotationChanged()) { out += "rotation"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 36ca47291c..0838bb937a 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -117,6 +117,7 @@ public: // bool _fooChanged { false }; DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool, ENTITY_ITEM_DEFAULT_VISIBLE); + DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW); DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3, ENTITY_ITEM_DEFAULT_DIMENSIONS); DEFINE_PROPERTY_REF(PROP_ROTATION, Rotation, rotation, glm::quat, ENTITY_ITEM_DEFAULT_ROTATION); @@ -426,6 +427,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Velocity, velocity, "in meters"); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Name, name, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Visible, visible, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CanCastShadow, canCastShadow, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Rotation, rotation, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Density, density, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Gravity, gravity, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index eb09a64628..0e0c2994cd 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -46,6 +46,7 @@ const quint32 ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION = 0; const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f; const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f; const bool ENTITY_ITEM_DEFAULT_VISIBLE = true; +const bool ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW { true }; const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString(""); const quint64 ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP = 0; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index b65d5d1a3f..07908fe6cf 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -20,6 +20,7 @@ enum EntityPropertyList { // these properties are supported by the EntityItem base class PROP_VISIBLE, + PROP_CAN_CAST_SHADOW, PROP_POSITION, PROP_DIMENSIONS, PROP_ROTATION, @@ -205,6 +206,11 @@ enum EntityPropertyList { PROP_HAZE_MODE, + PROP_KEYLIGHT_COLOR, + PROP_KEYLIGHT_INTENSITY, + PROP_KEYLIGHT_DIRECTION, + PROP_KEYLIGHT_CAST_SHADOW, + PROP_HAZE_RANGE, PROP_HAZE_COLOR, PROP_HAZE_GLARE_COLOR, @@ -254,10 +260,6 @@ enum EntityPropertyList { // Aliases/Piggyback properties for Zones. These properties intentionally reuse the enum values for // other properties which will never overlap with each other. We do this so that we don't have to expand // the size of the properties bitflags mask - PROP_KEYLIGHT_COLOR = PROP_COLOR, - PROP_KEYLIGHT_INTENSITY = PROP_INTENSITY, - PROP_KEYLIGHT_DIRECTION = PROP_EXPONENT, - PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, PROP_SKYBOX_URL = PROP_ANIMATION_FPS, diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 307371c649..694542b04e 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -97,6 +97,7 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const auto mutableProperties = properties; mutableProperties.markAllChanged(); newEntityItem = factory(entityID, mutableProperties); + newEntityItem->moveToThread(qApp->thread()); } return newEntityItem; } diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index c476b4c23c..b2c65c6f9d 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -21,6 +21,7 @@ const xColor KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; +const bool KeyLightPropertyGroup::DEFAULT_KEYLIGHT_CAST_SHADOWS { false }; void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { @@ -28,23 +29,27 @@ void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desired COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_CAST_SHADOW, KeyLight, keyLight, CastShadows, castShadows); } void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, xColor, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, glmVec3, setDirection); - + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, castShadows, bool, setCastShadows); + // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, xColor, setColor, getColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, glmVec3, setDirection, getDirection); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightCastShadows, bool, setCastShadows, getCastShadows); } void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) { COPY_PROPERTY_IF_CHANGED(color); COPY_PROPERTY_IF_CHANGED(intensity); COPY_PROPERTY_IF_CHANGED(direction); + COPY_PROPERTY_IF_CHANGED(castShadows); } void KeyLightPropertyGroup::debugDump() const { @@ -52,6 +57,7 @@ void KeyLightPropertyGroup::debugDump() const { qCDebug(entities) << " color:" << getColor(); // << "," << getColor()[1] << "," << getColor()[2]; qCDebug(entities) << " intensity:" << getIntensity(); qCDebug(entities) << " direction:" << getDirection(); + qCDebug(entities) << " castShadows:" << getCastShadows(); } void KeyLightPropertyGroup::listChangedProperties(QList& out) { @@ -64,6 +70,9 @@ void KeyLightPropertyGroup::listChangedProperties(QList& out) { if (directionChanged()) { out << "keyLight-direction"; } + if (castShadowsChanged()) { + out << "keyLight-castShadows"; + } } bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, @@ -71,19 +80,21 @@ bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, - OctreeElement::AppendState& appendState) const { + OctreeElement::AppendState& appendState) const +{ bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); - + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, getCastShadows()); + return true; } bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, - int& processedBytes) { + int& processedBytes) { int bytesRead = 0; bool overwriteLocalData = true; @@ -92,11 +103,13 @@ bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFl READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); - + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_DIRECTION, Direction); - + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_CAST_SHADOW, CastShadows); + processedBytes += bytesRead; Q_UNUSED(somethingChanged); @@ -108,6 +121,7 @@ void KeyLightPropertyGroup::markAllChanged() { _colorChanged = true; _intensityChanged = true; _directionChanged = true; + _castShadowsChanged = true; } EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const { @@ -116,7 +130,8 @@ EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, intensity); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, direction); - + CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_CAST_SHADOW, castShadows); + return changedProperties; } @@ -124,6 +139,7 @@ void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) cons COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Color, getColor); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Intensity, getIntensity); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Direction, getDirection); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, CastShadows, getCastShadows); } bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties) { @@ -132,6 +148,7 @@ bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Color, color, setColor); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Intensity, intensity, setIntensity); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Direction, direction, setDirection); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, CastShadows, castShadows, setCastShadows); return somethingChanged; } @@ -142,6 +159,7 @@ EntityPropertyFlags KeyLightPropertyGroup::getEntityProperties(EncodeBitstreamPa requestedProperties += PROP_KEYLIGHT_COLOR; requestedProperties += PROP_KEYLIGHT_INTENSITY; requestedProperties += PROP_KEYLIGHT_DIRECTION; + requestedProperties += PROP_KEYLIGHT_CAST_SHADOW; return requestedProperties; } @@ -159,6 +177,7 @@ void KeyLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, Enc APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, getCastShadows()); } int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -172,6 +191,7 @@ int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); return bytesRead; } diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index f33ebb282d..5e13a6afa6 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -78,10 +78,12 @@ public: static const float DEFAULT_KEYLIGHT_INTENSITY; static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; + static const bool DEFAULT_KEYLIGHT_CAST_SHADOWS; DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR); DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); + DEFINE_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, CastShadows, castShadows, bool, DEFAULT_KEYLIGHT_CAST_SHADOWS); }; #endif // hifi_KeyLightPropertyGroup_h diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index bec7bc1906..e4a3a958b6 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -53,6 +53,7 @@ void ModelEntityItem::setTextures(const QString& textures) { EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); @@ -64,6 +65,7 @@ EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredP COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations); COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints); _animationProperties.getProperties(properties); + return properties; } @@ -191,8 +193,6 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations()); } - - // added update function back for property fix void ModelEntityItem::update(const quint64& now) { @@ -571,15 +571,16 @@ QVector ModelEntityItem::getJointTranslationsSet() const { return result; } - xColor ModelEntityItem::getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } + bool ModelEntityItem::hasModel() const { return resultWithReadLock([&] { return !_modelURL.isEmpty(); }); } + bool ModelEntityItem::hasCompoundShapeURL() const { return !_compoundShapeURL.get().isEmpty(); } @@ -659,7 +660,6 @@ bool ModelEntityItem::getAnimationLoop() const { }); } - void ModelEntityItem::setAnimationHold(bool hold) { withWriteLock([&] { _animationProperties.setHold(hold); diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 2425208a87..7b4a452bcc 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -92,6 +92,7 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class properties.setColor(getXColor()); properties.setShape(entity::stringFromShape(getShape())); + return properties; } @@ -166,6 +167,7 @@ EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_SHAPE; requestedProperties += PROP_COLOR; requestedProperties += PROP_ALPHA; + return requestedProperties; } @@ -368,4 +370,3 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { ShapeType ShapeEntityItem::getShapeType() const { return _collisionShapeType; } - diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 7ad1b3c1c2..3011482184 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -114,6 +114,8 @@ protected: //! ellipsoids. ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID }; + bool _canCastShadow { ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW }; + std::shared_ptr _material; }; diff --git a/libraries/graphics/src/graphics/Light.cpp b/libraries/graphics/src/graphics/Light.cpp index 9da14fec4f..76d8a6030a 100755 --- a/libraries/graphics/src/graphics/Light.cpp +++ b/libraries/graphics/src/graphics/Light.cpp @@ -65,6 +65,14 @@ const Vec3& Light::getDirection() const { return _lightSchemaBuffer->volume.direction; } +void Light::setCastShadows(const bool castShadows) { + _castShadows = castShadows; +} + +bool Light::getCastShadows() const { + return _castShadows; +} + void Light::setColor(const Color& color) { _lightSchemaBuffer.edit().irradiance.color = color; updateLightRadius(); @@ -132,7 +140,6 @@ void Light::setSpotExponent(float exponent) { _lightSchemaBuffer.edit().irradiance.falloffSpot = exponent; } - void Light::setAmbientIntensity(float intensity) { _ambientSchemaBuffer.edit().intensity = intensity; } diff --git a/libraries/graphics/src/graphics/Light.h b/libraries/graphics/src/graphics/Light.h index e6ef1e35c5..bb9fb3e5b9 100755 --- a/libraries/graphics/src/graphics/Light.h +++ b/libraries/graphics/src/graphics/Light.h @@ -103,6 +103,9 @@ public: void setDirection(const Vec3& direction); const Vec3& getDirection() const; + void setCastShadows(const bool castShadows); + bool getCastShadows() const; + void setOrientation(const Quat& orientation); const glm::quat& getOrientation() const { return _transform.getRotation(); } @@ -191,6 +194,8 @@ protected: void updateLightRadius(); + bool _castShadows{ false }; + }; typedef std::shared_ptr< Light > LightPointer; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 5c202fa70c..a83924ee58 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::MaterialEntities); + return static_cast(EntityVersion::ShadowControl); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::RemovedJurisdictions); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 670324c4b7..ea785f24f2 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -218,7 +218,8 @@ enum class EntityVersion : PacketVersion { ZoneLightInheritModes = 82, ZoneStageRemoved, SoftEntities, - MaterialEntities + MaterialEntities, + ShadowControl }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 55643985c8..af9ff76eb3 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -328,14 +328,14 @@ void Socket::checkForReadyReadBackup() { void Socket::readPendingDatagrams() { int packetSizeWithHeader = -1; - while ((packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) { + while (_udpSocket.hasPendingDatagrams() && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) { // we're reading a packet so re-start the readyRead backup timer _readyReadBackupTimer->start(); // grab a time point we can mark as the receive time of this packet auto receiveTime = p_high_resolution_clock::now(); - + // setup a HifiSockAddr to read into HifiSockAddr senderSockAddr; diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 41a5bf5faf..a4dc56e696 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -20,16 +20,32 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { - ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices, + const std::vector& cauterizedClusterMatrices) { + ModelMeshPartPayload::updateClusterBuffer(clusterMatrices); - if (cauterizedClusterTransforms.size() > 1) { + if (cauterizedClusterMatrices.size() > 1) { if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(TransformType), - (const gpu::Byte*) cauterizedClusterTransforms.data()); + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterMatrices.data()); } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(TransformType), - (const gpu::Byte*) cauterizedClusterTransforms.data()); + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterMatrices.data()); + } + } +} + +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterDualQuaternions, + const std::vector& cauterizedClusterDualQuaternions) { + ModelMeshPartPayload::updateClusterBuffer(clusterDualQuaternions); + + if (cauterizedClusterDualQuaternions.size() > 1) { + if (!_cauterizedClusterBuffer) { + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), + (const gpu::Byte*) cauterizedClusterDualQuaternions.data()); + } else { + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), + (const gpu::Byte*) cauterizedClusterDualQuaternions.data()); } } } diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 3c0f90fcb5..9a6cea8b9f 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -15,13 +15,13 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); -#if defined(SKIN_DQ) - using TransformType = Model::TransformDualQuaternion; -#else - using TransformType = glm::mat4; -#endif + // matrix palette skinning + void updateClusterBuffer(const std::vector& clusterMatrices, + const std::vector& cauterizedClusterMatrices); - void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); + // dual quaternion skinning + void updateClusterBuffer(const std::vector& clusterDualQuaternions, + const std::vector& cauterizedClusterQuaternions); void updateTransformForCauterizedMesh(const Transform& renderTransform); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 54dfd96a00..fdb57dace7 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -35,8 +35,13 @@ bool CauterizedModel::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { Model::MeshState state; - state.clusterTransforms.resize(mesh.clusters.size()); - _cauterizeMeshStates.append(state); + if (_useDualQuaternionSkinning) { + state.clusterDualQuaternions.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } else { + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } } } return needsFullUpdate; @@ -109,33 +114,33 @@ void CauterizedModel::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); -#if defined(SKIN_DQ) - auto jointPose = _rig.getJointPose(cluster.jointIndex); - Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); - Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); - state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); - state.clusterTransforms[j].setCauterizationParameters(0.0f, jointPose.trans()); -#else - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); -#endif + if (_useDualQuaternionSkinning) { + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); + } else { + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + } } } // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { -#if defined(SKIN_DQ) + AnimPose cauterizePose = _rig.getJointPose(geometry.neckJointIndex); cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); -#else + static const glm::mat4 zeroScale( glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; -#endif + for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -143,19 +148,24 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { - // not cauterized so just copy the value from the non-cauterized version. - state.clusterTransforms[j] = _meshStates[i].clusterTransforms[j]; + if (_useDualQuaternionSkinning) { + if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { + // not cauterized so just copy the value from the non-cauterized version. + state.clusterDualQuaternions[j] = _meshStates[i].clusterDualQuaternions[j]; + } else { + Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); + } } else { -#if defined(SKIN_DQ) - Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); - Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); - state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); - state.clusterTransforms[j].setCauterizationParameters(1.0f, cauterizePose.trans()); -#else - glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); -#endif + if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { + // not cauterized so just copy the value from the non-cauterized version. + state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; + } else { + glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + } } } } @@ -204,6 +214,7 @@ void CauterizedModel::updateRenderItems() { bool isWireframe = self->isWireframe(); bool isVisible = self->isVisible(); + bool canCastShadow = self->canCastShadow(); bool isLayeredInFront = self->isLayeredInFront(); bool isLayeredInHUD = self->isLayeredInHUD(); bool enableCauterization = self->getEnableCauterization(); @@ -213,45 +224,59 @@ void CauterizedModel::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); - auto clusterTransformsCauterized(self->getCauterizeMeshState(meshIndex).clusterTransforms); + + const auto& meshState = self->getMeshState(meshIndex); + const auto& cauterizedMeshState = self->getCauterizeMeshState(meshIndex); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); + bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); - transaction.updateItem(itemID, [modelTransform, clusterTransforms, clusterTransformsCauterized, invalidatePayloadShapeKey, - isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) { - data.updateClusterBuffer(clusterTransforms, clusterTransformsCauterized); + transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, + isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, canCastShadow, enableCauterization](CauterizedMeshPartPayload& data) { + if (useDualQuaternionSkinning) { + data.updateClusterBuffer(meshState.clusterDualQuaternions, + cauterizedMeshState.clusterDualQuaternions); + } else { + data.updateClusterBuffer(meshState.clusterMatrices, + cauterizedMeshState.clusterMatrices); + } Transform renderTransform = modelTransform; - if (clusterTransforms.size() == 1) { -#if defined(SKIN_DQ) - Transform transform(clusterTransforms[0].getRotation(), - clusterTransforms[0].getScale(), - clusterTransforms[0].getTranslation()); - renderTransform = modelTransform.worldTransform(transform); -#else - renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); -#endif + if (useDualQuaternionSkinning) { + if (meshState.clusterDualQuaternions.size() == 1) { + const auto& dq = meshState.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = modelTransform.worldTransform(transform); + } + } else { + if (meshState.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0])); + } } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); renderTransform = modelTransform; - if (clusterTransformsCauterized.size() == 1) { -#if defined(SKIN_DQ) - Transform transform(clusterTransformsCauterized[0].getRotation(), - clusterTransformsCauterized[0].getScale(), - clusterTransformsCauterized[0].getTranslation()); - renderTransform = modelTransform.worldTransform(Transform(transform)); -#else - renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); -#endif + if (useDualQuaternionSkinning) { + if (cauterizedMeshState.clusterDualQuaternions.size() == 1) { + const auto& dq = cauterizedMeshState.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); + } + } else { + if (cauterizedMeshState.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(cauterizedMeshState.clusterMatrices[0])); + } } data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::TAG_BITS_ALL); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, render::ItemKey::TAG_BITS_ALL); data.setLayer(isLayeredInFront, isLayeredInHUD); - data.setShapeKey(invalidatePayloadShapeKey, isWireframe); + data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning); }); } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 6988a915a1..fc35267ddc 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -492,7 +492,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, deferredFramebuffer->getPrimaryDepthTexture()); // FIXME: Different render modes should have different tasks - if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && deferredLightingEffect->isAmbientOcclusionEnabled()) { + if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && deferredLightingEffect->isAmbientOcclusionEnabled() && ambientOcclusionFramebuffer) { batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, ambientOcclusionFramebuffer->getOcclusionTexture()); } else { // need to assign the white texture if ao is off @@ -537,15 +537,25 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, auto keyLight = lightAndShadow.first; - graphics::LightPointer keyAmbientLight; + graphics::LightPointer ambientLight; if (lightStage && lightStage->_currentFrame._ambientLights.size()) { - keyAmbientLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front()); + ambientLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front()); } - bool hasAmbientMap = (keyAmbientLight != nullptr); + bool hasAmbientMap = (ambientLight != nullptr); // Setup the global directional pass pipeline { - if (deferredLightingEffect->_shadowMapEnabled) { + // Check if keylight casts shadows + bool keyLightCastShadows { false }; + + if (lightStage && lightStage->_currentFrame._sunLights.size()) { + graphics::LightPointer keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front()); + if (keyLight) { + keyLightCastShadows = keyLight->getCastShadows(); + } + } + + if (deferredLightingEffect->_shadowMapEnabled && keyLightCastShadows) { // If the keylight has an ambient Map then use the Skybox version of the pass // otherwise use the ambient sphere version diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 1b776e6409..ce7ecacbbe 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -61,7 +61,7 @@ public: private: DeferredLightingEffect() = default; - bool _shadowMapEnabled{ false }; + bool _shadowMapEnabled{ true }; // note that this value is overwritten in the ::configure method bool _ambientOcclusionEnabled{ false }; graphics::MeshPointer _pointLightMesh; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 07822c6103..98ecc4c941 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -77,7 +77,7 @@ void MeshPartPayload::removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } -void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled) { +void MeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) { ItemKey::Builder builder; builder.withTypeShape(); @@ -91,6 +91,10 @@ void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits, builder.withLayered(); } + if (canCastShadow) { + builder.withShadowCaster(); + } + if (isGroupCulled) { builder.withSubMetaCulled(); } @@ -222,25 +226,35 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in _shapeID(shapeIndex) { assert(model && model->isLoaded()); + + bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); + _blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex]; auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); - computeAdjustedLocalBound(state.clusterTransforms); + + if (useDualQuaternionSkinning) { + computeAdjustedLocalBound(state.clusterDualQuaternions); + } else { + computeAdjustedLocalBound(state.clusterMatrices); + } updateTransform(transform, offsetTransform); Transform renderTransform = transform; - if (state.clusterTransforms.size() == 1) { -#if defined(SKIN_DQ) - Transform transform(state.clusterTransforms[0].getRotation(), - state.clusterTransforms[0].getScale(), - state.clusterTransforms[0].getTranslation()); - renderTransform = transform.worldTransform(Transform(transform)); -#else - renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); -#endif - + if (useDualQuaternionSkinning) { + if (state.clusterDualQuaternions.size() == 1) { + const auto& dq = state.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = transform.worldTransform(Transform(transform)); + } + } else { + if (state.clusterMatrices.size() == 1) { + renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); + } } updateTransformForSkinnedMesh(renderTransform, transform); @@ -270,16 +284,44 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices) { + + // reset cluster buffer if we change the cluster buffer type + if (_clusterBufferType != ClusterBufferType::Matrices) { + _clusterBuffer.reset(); + } + _clusterBufferType = ClusterBufferType::Matrices; + // Once computed the cluster matrices, update the buffer(s) - if (clusterTransforms.size() > 1) { + if (clusterMatrices.size() > 1) { if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(TransformType), - (const gpu::Byte*) clusterTransforms.data()); + _clusterBuffer = std::make_shared(clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterMatrices.data()); } else { - _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(TransformType), - (const gpu::Byte*) clusterTransforms.data()); + _clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterMatrices.data()); + } + } +} + +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterDualQuaternions) { + + // reset cluster buffer if we change the cluster buffer type + if (_clusterBufferType != ClusterBufferType::DualQuaternions) { + _clusterBuffer.reset(); + } + _clusterBufferType = ClusterBufferType::DualQuaternions; + + // Once computed the cluster matrices, update the buffer(s) + if (clusterDualQuaternions.size() > 1) { + if (!_clusterBuffer) { + _clusterBuffer = std::make_shared(clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), + (const gpu::Byte*) clusterDualQuaternions.data()); + } + else { + _clusterBuffer->setSubData(0, clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), + (const gpu::Byte*) clusterDualQuaternions.data()); } } } @@ -290,7 +332,8 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render _worldBound.transform(boundTransform); } -void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled) { +// Note that this method is called for models but not for shapes +void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) { ItemKey::Builder builder; builder.withTypeShape(); @@ -304,6 +347,10 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tag builder.withLayered(); } + if (canCastShadow) { + builder.withShadowCaster(); + } + if (isGroupCulled) { builder.withSubMetaCulled(); } @@ -336,7 +383,7 @@ int ModelMeshPartPayload::getLayer() const { return _layer; } -void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe) { +void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning) { if (invalidateShapeKey) { _shapeKey = ShapeKey::Builder::invalid(); return; @@ -383,6 +430,10 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe if (isWireframe) { builder.withWireframe(); } + if (isSkinned && useDualQuaternionSkinning) { + builder.withDualQuatSkinned(); + } + _shapeKey = builder.build(); } @@ -438,29 +489,33 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } - -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterMatrices) { _adjustedLocalBound = _localBound; - if (clusterTransforms.size() > 0) { -#if defined(SKIN_DQ) - Transform rootTransform(clusterTransforms[0].getRotation(), - clusterTransforms[0].getScale(), - clusterTransforms[0].getTranslation()); - _adjustedLocalBound.transform(rootTransform); -#else - _adjustedLocalBound.transform(clusterTransforms[0]); -#endif + if (clusterMatrices.size() > 0) { + _adjustedLocalBound.transform(clusterMatrices[0]); - for (int i = 1; i < (int)clusterTransforms.size(); ++i) { + for (int i = 1; i < (int)clusterMatrices.size(); ++i) { AABox clusterBound = _localBound; -#if defined(SKIN_DQ) - Transform transform(clusterTransforms[i].getRotation(), - clusterTransforms[i].getScale(), - clusterTransforms[i].getTranslation()); - clusterBound.transform(transform); -#else - clusterBound.transform(clusterTransforms[i]); -#endif + clusterBound.transform(clusterMatrices[i]); + _adjustedLocalBound += clusterBound; + } + } +} + +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterDualQuaternions) { + _adjustedLocalBound = _localBound; + if (clusterDualQuaternions.size() > 0) { + Transform rootTransform(clusterDualQuaternions[0].getRotation(), + clusterDualQuaternions[0].getScale(), + clusterDualQuaternions[0].getTranslation()); + _adjustedLocalBound.transform(rootTransform); + + for (int i = 1; i < (int)clusterDualQuaternions.size(); ++i) { + AABox clusterBound = _localBound; + Transform transform(clusterDualQuaternions[i].getRotation(), + clusterDualQuaternions[i].getScale(), + clusterDualQuaternions[i].getTranslation()); + clusterBound.transform(transform); _adjustedLocalBound += clusterBound; } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 7d7b834fc1..f1877d9ef6 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -32,7 +32,7 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - virtual void updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled = false); + virtual void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false); virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); @@ -92,14 +92,14 @@ public: void notifyLocationChanged() override; -#if defined(SKIN_DQ) - using TransformType = Model::TransformDualQuaternion; -#else - using TransformType = glm::mat4; -#endif + void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false) override; + + // matrix palette skinning + void updateClusterBuffer(const std::vector& clusterMatrices); + + // dual quaternion skinning + void updateClusterBuffer(const std::vector& clusterDualQuaternions); - void updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled = false) override; - void updateClusterBuffer(const std::vector& clusterTransforms); void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface @@ -108,16 +108,23 @@ public: void render(RenderArgs* args) override; void setLayer(bool isLayeredInFront, bool isLayeredInHUD); - void setShapeKey(bool invalidateShapeKey, bool isWireframe); + void setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning); // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const override; - void computeAdjustedLocalBound(const std::vector& clusterTransforms); + // matrix palette skinning + void computeAdjustedLocalBound(const std::vector& clusterMatrices); + + // dual quaternion skinning + void computeAdjustedLocalBound(const std::vector& clusterDualQuaternions); gpu::BufferPointer _clusterBuffer; + enum class ClusterBufferType { Matrices, DualQuaternions }; + ClusterBufferType _clusterBufferType { ClusterBufferType::Matrices }; + int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 18308d8df5..79aa3dcf68 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -102,6 +102,7 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : _snappedToRegistrationPoint(false), _url(HTTP_INVALID_COM), _isVisible(true), + _canCastShadow(false), _blendNumber(0), _appliedBlendNumber(0), _isWireframe(false) @@ -268,6 +269,7 @@ void Model::updateRenderItems() { bool isWireframe = self->isWireframe(); bool isVisible = self->isVisible(); + bool canCastShadow = self->canCastShadow(); uint8_t viewTagBits = self->getViewTagBits(); bool isLayeredInFront = self->isLayeredInFront(); bool isLayeredInHUD = self->isLayeredInHUD(); @@ -278,32 +280,42 @@ void Model::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); + + const auto& meshState = self->getMeshState(meshIndex); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); + bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); - transaction.updateItem(itemID, [modelTransform, clusterTransforms, + transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, invalidatePayloadShapeKey, isWireframe, isVisible, - viewTagBits, isLayeredInFront, + canCastShadow, viewTagBits, isLayeredInFront, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateClusterBuffer(clusterTransforms); + if (useDualQuaternionSkinning) { + data.updateClusterBuffer(meshState.clusterDualQuaternions); + } else { + data.updateClusterBuffer(meshState.clusterMatrices); + } Transform renderTransform = modelTransform; - if (clusterTransforms.size() == 1) { -#if defined(SKIN_DQ) - Transform transform(clusterTransforms[0].getRotation(), - clusterTransforms[0].getScale(), - clusterTransforms[0].getTranslation()); - renderTransform = modelTransform.worldTransform(Transform(transform)); -#else - renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); -#endif + + if (useDualQuaternionSkinning) { + if (meshState.clusterDualQuaternions.size() == 1) { + const auto& dq = meshState.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); + } + } else { + if (meshState.clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0])); + } } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); data.setLayer(isLayeredInFront, isLayeredInHUD); - data.setShapeKey(invalidatePayloadShapeKey, isWireframe); + data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning); }); } @@ -378,7 +390,8 @@ bool Model::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; - state.clusterTransforms.resize(mesh.clusters.size()); + state.clusterDualQuaternions.resize(mesh.clusters.size()); + state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.push_back(state); // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index @@ -693,46 +706,66 @@ void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, bool isLayeredInFront = _isLayeredInFront; bool isLayeredInHUD = _isLayeredInHUD; - + bool canCastShadow = _canCastShadow; render::Transaction transaction; foreach (auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); }); } scene->enqueueTransaction(transaction); } } +void Model::setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled) { + if (_canCastShadow != canCastShadow) { + _canCastShadow = canCastShadow; + + bool isVisible = _isVisible; + bool isLayeredInFront = _isLayeredInFront; + bool isLayeredInHUD = _isLayeredInHUD; + + render::Transaction transaction; + foreach (auto item, _modelMeshRenderItemsMap.keys()) { + transaction.updateItem(item, + [isVisible, viewTagBits, canCastShadow, isLayeredInFront, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { + data.updateKey(isVisible, viewTagBits, canCastShadow, isLayeredInFront || isLayeredInHUD, isGroupCulled); + }); + } + + scene->enqueueTransaction(transaction); + } +} void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene) { if (_isLayeredInFront != isLayeredInFront) { _isLayeredInFront = isLayeredInFront; bool isVisible = _isVisible; + bool canCastShadow = _canCastShadow; uint8_t viewTagBits = _viewTagBits; bool isLayeredInHUD = _isLayeredInHUD; bool isGroupCulled = _isGroupCulled; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } @@ -745,22 +778,23 @@ void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& sce _isLayeredInHUD = isLayeredInHUD; bool isVisible = _isVisible; + bool canCastShadow = _canCastShadow; uint8_t viewTagBits = _viewTagBits; bool isLayeredInFront = _isLayeredInFront; bool isGroupCulled = _isGroupCulled; render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, + transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled); + data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); data.setLayer(isLayeredInFront, isLayeredInHUD); }); } @@ -1234,6 +1268,10 @@ void Model::snapToRegistrationPoint() { _snappedToRegistrationPoint = true; } +void Model::setUseDualQuaternionSkinning(bool value) { + _useDualQuaternionSkinning = value; +} + void Model::simulate(float deltaTime, bool fullUpdate) { DETAILED_PROFILE_RANGE(simulation_detail, __FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) @@ -1267,7 +1305,11 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { void Model::computeMeshPartLocalBounds() { for (auto& part : _modelMeshRenderItems) { const Model::MeshState& state = _meshStates.at(part->_meshIndex); - part->computeAdjustedLocalBound(state.clusterTransforms); + if (_useDualQuaternionSkinning) { + part->computeAdjustedLocalBound(state.clusterDualQuaternions); + } else { + part->computeAdjustedLocalBound(state.clusterMatrices); + } } } @@ -1286,16 +1328,16 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); -#if defined(SKIN_DQ) - auto jointPose = _rig.getJointPose(cluster.jointIndex); - Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); - Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); - state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); -#else - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); -#endif + if (_useDualQuaternionSkinning) { + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); + } else { + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + } } } @@ -1562,15 +1604,17 @@ void Model::addMaterial(graphics::MaterialLayer material, const std::string& par uint8_t viewTagBits = getViewTagBits(); bool layeredInFront = isLayeredInFront(); bool layeredInHUD = isLayeredInHUD(); + bool canCastShadow = _canCastShadow; bool wireframe = isWireframe(); auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, - invalidatePayloadShapeKey, wireframe](ModelMeshPartPayload& data) { + bool useDualQuaternionSkinning = _useDualQuaternionSkinning; + transaction.updateItem(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow, + invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) { data.addMaterial(material); // if the material changed, we might need to update our item key or shape key - data.updateKey(visible, layeredInFront || layeredInHUD, viewTagBits); - data.setShapeKey(invalidatePayloadShapeKey, wireframe); + data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits); + data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning); }); } } @@ -1587,15 +1631,17 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string uint8_t viewTagBits = getViewTagBits(); bool layeredInFront = isLayeredInFront(); bool layeredInHUD = isLayeredInHUD(); + bool canCastShadow = _canCastShadow; bool wireframe = isWireframe(); auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, - invalidatePayloadShapeKey, wireframe](ModelMeshPartPayload& data) { + bool useDualQuaternionSkinning = _useDualQuaternionSkinning; + transaction.updateItem(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow, + invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) { data.removeMaterial(material); // if the material changed, we might need to update our item key or shape key - data.updateKey(visible, layeredInFront || layeredInHUD, viewTagBits); - data.setShapeKey(invalidatePayloadShapeKey, wireframe); + data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits); + data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning); }); } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index cfcf0a2ce6..3d532f7d7c 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -87,6 +87,10 @@ public: // new Scene/Engine rendering support void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled); + + bool canCastShadow() const { return _canCastShadow; } + void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled); + void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene); void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene); bool needsFixupInScene() const; @@ -256,8 +260,6 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } - -#if defined(SKIN_DQ) class TransformDualQuaternion { public: TransformDualQuaternion() {} @@ -295,15 +297,11 @@ public: DualQuaternion _dq; glm::vec4 _cauterizedPosition { 0.0f, 0.0f, 0.0f, 1.0f }; }; -#endif class MeshState { public: -#if defined(SKIN_DQ) - std::vector clusterTransforms; -#else - std::vector clusterTransforms; -#endif + std::vector clusterDualQuaternions; + std::vector clusterMatrices; }; const MeshState& getMeshState(int index) { return _meshStates.at(index); } @@ -319,6 +317,8 @@ public: Q_INVOKABLE MeshProxyList getMeshes() const; void scaleToFit(); + bool getUseDualQuaternionSkinning() const { return _useDualQuaternionSkinning; } + void setUseDualQuaternionSkinning(bool value); void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); @@ -404,6 +404,8 @@ protected: bool _isVisible; uint8_t _viewTagBits{ render::ItemKey::TAG_BITS_ALL }; + bool _canCastShadow; + gpu::Buffers _blendedVertexBuffers; QVector > > _dilatedTextures; @@ -425,6 +427,7 @@ protected: virtual void createCollisionRenderItemSet(); bool _isWireframe; + bool _useDualQuaternionSkinning { false }; // debug rendering support int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 3c80a2d14c..3fedae1778 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -26,6 +26,8 @@ #include "model_lightmap_normal_map_vert.h" #include "skin_model_vert.h" #include "skin_model_normal_map_vert.h" +#include "skin_model_dq_vert.h" +#include "skin_model_normal_map_dq_vert.h" #include "model_lightmap_fade_vert.h" #include "model_lightmap_normal_map_fade_vert.h" @@ -33,6 +35,8 @@ #include "model_translucent_normal_map_vert.h" #include "skin_model_fade_vert.h" #include "skin_model_normal_map_fade_vert.h" +#include "skin_model_fade_dq_vert.h" +#include "skin_model_normal_map_fade_dq_vert.h" #include "simple_vert.h" #include "simple_textured_frag.h" @@ -95,6 +99,7 @@ #include "model_shadow_vert.h" #include "skin_model_shadow_vert.h" +#include "skin_model_shadow_dq_vert.h" #include "model_shadow_frag.h" #include "skin_model_shadow_frag.h" @@ -195,16 +200,28 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelTranslucentVertex = model_translucent_vert::getShader(); auto modelTranslucentNormalMapVertex = model_translucent_normal_map_vert::getShader(); auto modelShadowVertex = model_shadow_vert::getShader(); + + auto modelLightmapFadeVertex = model_lightmap_fade_vert::getShader(); + auto modelLightmapNormalMapFadeVertex = model_lightmap_normal_map_fade_vert::getShader(); + + // matrix palette skinned auto skinModelVertex = skin_model_vert::getShader(); auto skinModelNormalMapVertex = skin_model_normal_map_vert::getShader(); auto skinModelShadowVertex = skin_model_shadow_vert::getShader(); - auto modelLightmapFadeVertex = model_lightmap_fade_vert::getShader(); - auto modelLightmapNormalMapFadeVertex = model_lightmap_normal_map_fade_vert::getShader(); auto skinModelFadeVertex = skin_model_fade_vert::getShader(); auto skinModelNormalMapFadeVertex = skin_model_normal_map_fade_vert::getShader(); auto skinModelTranslucentVertex = skinModelFadeVertex; // We use the same because it ouputs world position per vertex auto skinModelNormalMapTranslucentVertex = skinModelNormalMapFadeVertex; // We use the same because it ouputs world position per vertex + // dual quaternion skinned + auto skinModelDualQuatVertex = skin_model_dq_vert::getShader(); + auto skinModelNormalMapDualQuatVertex = skin_model_normal_map_dq_vert::getShader(); + auto skinModelShadowDualQuatVertex = skin_model_shadow_dq_vert::getShader(); + auto skinModelFadeDualQuatVertex = skin_model_fade_dq_vert::getShader(); + auto skinModelNormalMapFadeDualQuatVertex = skin_model_normal_map_fade_dq_vert::getShader(); + auto skinModelTranslucentDualQuatVertex = skinModelFadeDualQuatVertex; // We use the same because it ouputs world position per vertex + auto skinModelNormalMapTranslucentDualQuatVertex = skinModelNormalMapFadeDualQuatVertex; // We use the same because it ouputs world position per vertex + auto modelFadeVertex = model_fade_vert::getShader(); auto modelNormalMapFadeVertex = model_normal_map_fade_vert::getShader(); auto simpleFadeVertex = simple_fade_vert::getShader(); @@ -376,7 +393,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip Key::Builder().withMaterial().withLightmap().withTangents().withSpecular().withFade(), modelLightmapNormalMapFadeVertex, modelLightmapNormalSpecularMapFadePixel, batchSetter, itemSetter); - // Skinned + // matrix palette skinned addPipeline( Key::Builder().withMaterial().withSkinned(), skinModelVertex, modelPixel, nullptr, nullptr); @@ -403,7 +420,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip Key::Builder().withMaterial().withSkinned().withTangents().withSpecular().withFade(), skinModelNormalMapFadeVertex, modelNormalSpecularMapFadePixel, batchSetter, itemSetter); - // Skinned and Translucent + // matrix palette skinned and translucent addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent(), skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); @@ -430,6 +447,60 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular().withFade(), skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); + // dual quaternion skinned + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned(), + skinModelDualQuatVertex, modelPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents(), + skinModelNormalMapDualQuatVertex, modelNormalMapPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withSpecular(), + skinModelDualQuatVertex, modelSpecularMapPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents().withSpecular(), + skinModelNormalMapDualQuatVertex, modelNormalSpecularMapPixel, nullptr, nullptr); + // Same thing but with Fade on + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withFade(), + skinModelFadeDualQuatVertex, modelFadePixel, batchSetter, itemSetter); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents().withFade(), + skinModelNormalMapFadeDualQuatVertex, modelNormalMapFadePixel, batchSetter, itemSetter); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withSpecular().withFade(), + skinModelFadeDualQuatVertex, modelSpecularMapFadePixel, batchSetter, itemSetter); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents().withSpecular().withFade(), + skinModelNormalMapFadeDualQuatVertex, modelNormalSpecularMapFadePixel, batchSetter, itemSetter); + + // dual quaternion skinned and translucent + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent(), + skinModelTranslucentDualQuatVertex, modelTranslucentPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents(), + skinModelNormalMapTranslucentDualQuatVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withSpecular(), + skinModelTranslucentDualQuatVertex, modelTranslucentPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents().withSpecular(), + skinModelNormalMapTranslucentDualQuatVertex, modelTranslucentNormalMapPixel, nullptr, nullptr); + // Same thing but with Fade on + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withFade(), + skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents().withFade(), + skinModelNormalMapFadeDualQuatVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withSpecular().withFade(), + skinModelFadeDualQuatVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + addPipeline( + Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents().withSpecular().withFade(), + skinModelNormalMapFadeDualQuatVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter); + // Depth-only addPipeline( Key::Builder().withDepthOnly(), diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 13e68c6776..e4aebed142 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -218,7 +218,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende const auto setupOutput = task.addJob("ShadowSetup"); const auto queryResolution = setupOutput.getN(2); // Fetch and cull the items from the scene - static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask); + + // Enable models to not cast shadows (otherwise, models will always cast shadows) + static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask).withShadowCaster(); + const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying(); const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput); const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying(); @@ -297,8 +300,14 @@ void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) { } void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { + // Abort all jobs if not casting shadows auto lightStage = renderContext->_scene->getStage(); assert(lightStage); + if (!lightStage->getCurrentKeyLight() || !lightStage->getCurrentKeyLight()->getCastShadows()) { + renderContext->taskFlow.abortTask(); + return; + } + // Cache old render args RenderArgs* args = renderContext->args; @@ -378,12 +387,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); + // Cache old render args RenderArgs* args = renderContext->args; const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask); + output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask).withShadowCaster(); // Set the keylight render args auto& cascade = globalShadow->getCascade(_cascadeIndex); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 7f127a558c..98b70c0c9f 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -38,7 +38,7 @@ 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", false) {} + RenderShadowTaskConfig() : render::Task::Config::Persistent(QStringList() << "Render" << "Engine" << "Shadows", true) {} signals: void dirty(); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index abb04a4498..36eb35c757 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -89,7 +89,9 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); - vec4 cascadeShadowCoords[2] = { vec4(0), vec4(0) }; + vec4 cascadeShadowCoords[2]; + cascadeShadowCoords[0] = vec4(0); + cascadeShadowCoords[1] = vec4(0); ivec2 cascadeIndices; float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 6048ba4ade..fbfe6b7185 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -11,18 +11,16 @@ <@if not SKINNING_SLH@> <@def SKINNING_SLH@> -// Use dual quaternion skinning -// Must match #define SKIN_DQ in Model.h -<@def SKIN_DQ@> - const int MAX_CLUSTERS = 128; const int INDICES_PER_VERTEX = 4; +<@func declareUseDualQuaternionSkinning(USE_DUAL_QUATERNION_SKINNING)@> + layout(std140) uniform skinClusterBuffer { mat4 clusterMatrices[MAX_CLUSTERS]; }; -<@if SKIN_DQ@> +<@if USE_DUAL_QUATERNION_SKINNING@> mat4 dualQuatToMat4(vec4 real, vec4 dual) { float twoRealXSq = 2.0 * real.x * real.x; @@ -211,7 +209,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v skinnedTangent = vec3(m * vec4(inTangent, 0)); } -<@else@> // SKIN_DQ +<@else@> // USE_DUAL_QUATERNION_SKINNING void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); @@ -260,6 +258,8 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v skinnedTangent = newTangent.xyz; } -<@endif@> // if SKIN_DQ +<@endif@> // if USE_DUAL_QUATERNION_SKINNING + +<@endfunc@> // func declareUseDualQuaternionSkinning(USE_DUAL_QUATERNION_SKINNING) <@endif@> // if not SKINNING_SLH diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 0d0db7cbe3..079e6f75ef 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -52,27 +52,27 @@ void SoftAttachmentModel::updateClusterMatrices() { // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); -#if defined(SKIN_DQ) - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); - } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } + if (_useDualQuaternionSkinning) { + glm::mat4 jointMatrix; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - state.clusterTransforms[j] = Model::TransformDualQuaternion(m); -#else - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } + glm::mat4 jointMatrix; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); -#endif + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + } } } diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index 46ed45e9be..480e48a5d4 100644 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -18,6 +18,7 @@ <$declareStandardTransform()$> <@include Skinning.slh@> +<$declareUseDualQuaternionSkinning()$> <@include MaterialTextures.slh@> <$declareMaterialTexMapArrayBuffer()$> diff --git a/libraries/render-utils/src/skin_model_dq.slv b/libraries/render-utils/src/skin_model_dq.slv new file mode 100644 index 0000000000..96f9b4a713 --- /dev/null +++ b/libraries/render-utils/src/skin_model_dq.slv @@ -0,0 +1,52 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/14/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include Skinning.slh@> +<$declareUseDualQuaternionSkinning(1)$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out vec4 _position; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec3 _normal; +out vec3 _color; +out float _alpha; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + vec3 interpolatedNormal = vec3(0.0, 0.0, 0.0); + + skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); + + // pass along the color + _color = colorToLinearRGB(inColor.rgb); + _alpha = inColor.a; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> + <$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normal.xyz)$> +} diff --git a/libraries/render-utils/src/skin_model_fade.slv b/libraries/render-utils/src/skin_model_fade.slv index ca4500a18a..4f459d75f3 100644 --- a/libraries/render-utils/src/skin_model_fade.slv +++ b/libraries/render-utils/src/skin_model_fade.slv @@ -18,6 +18,7 @@ <$declareStandardTransform()$> <@include Skinning.slh@> +<$declareUseDualQuaternionSkinning()$> <@include MaterialTextures.slh@> <$declareMaterialTexMapArrayBuffer()$> diff --git a/libraries/render-utils/src/skin_model_fade_dq.slv b/libraries/render-utils/src/skin_model_fade_dq.slv new file mode 100644 index 0000000000..4f8a923a03 --- /dev/null +++ b/libraries/render-utils/src/skin_model_fade_dq.slv @@ -0,0 +1,54 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model_fade.vert +// vertex shader +// +// Created by Olivier Prat on 06/045/17. +// 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 +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include Skinning.slh@> +<$declareUseDualQuaternionSkinning(1)$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out vec4 _position; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec3 _normal; +out vec3 _color; +out float _alpha; +out vec4 _worldPosition; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + vec3 interpolatedNormal = vec3(0.0, 0.0, 0.0); + + skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); + + // pass along the color + _color = colorToLinearRGB(inColor.rgb); + _alpha = inColor.a; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> + <$transformModelToWorldPos(obj, position, _worldPosition)$> + <$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normal.xyz)$> +} diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index 3bf057c3c1..b54c84e5b3 100644 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -18,6 +18,7 @@ <$declareStandardTransform()$> <@include Skinning.slh@> +<$declareUseDualQuaternionSkinning()$> <@include MaterialTextures.slh@> <$declareMaterialTexMapArrayBuffer()$> diff --git a/libraries/render-utils/src/skin_model_normal_map_dq.slv b/libraries/render-utils/src/skin_model_normal_map_dq.slv new file mode 100644 index 0000000000..02b3742f6f --- /dev/null +++ b/libraries/render-utils/src/skin_model_normal_map_dq.slv @@ -0,0 +1,61 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model_normal_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/29/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include Skinning.slh@> +<$declareUseDualQuaternionSkinning(1)$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out vec4 _position; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec3 _normal; +out vec3 _tangent; +out vec3 _color; +out float _alpha; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0); + + skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); + + // pass along the color + _color = colorToLinearRGB(inColor.rgb); + _alpha = inColor.a; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); + interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> + <$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> + <$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$> + + _normal = interpolatedNormal.xyz; + _tangent = interpolatedTangent.xyz; +} diff --git a/libraries/render-utils/src/skin_model_normal_map_fade.slv b/libraries/render-utils/src/skin_model_normal_map_fade.slv index 032ef001d9..0e788b81b5 100644 --- a/libraries/render-utils/src/skin_model_normal_map_fade.slv +++ b/libraries/render-utils/src/skin_model_normal_map_fade.slv @@ -18,6 +18,7 @@ <$declareStandardTransform()$> <@include Skinning.slh@> +<$declareUseDualQuaternionSkinning()$> <@include MaterialTextures.slh@> <$declareMaterialTexMapArrayBuffer()$> diff --git a/libraries/render-utils/src/skin_model_normal_map_fade_dq.slv b/libraries/render-utils/src/skin_model_normal_map_fade_dq.slv new file mode 100644 index 0000000000..02b3742f6f --- /dev/null +++ b/libraries/render-utils/src/skin_model_normal_map_fade_dq.slv @@ -0,0 +1,61 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model_normal_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/29/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include Skinning.slh@> +<$declareUseDualQuaternionSkinning(1)$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out vec4 _position; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec3 _normal; +out vec3 _tangent; +out vec3 _color; +out float _alpha; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0); + + skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); + + // pass along the color + _color = colorToLinearRGB(inColor.rgb); + _alpha = inColor.a; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); + interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> + <$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> + <$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$> + + _normal = interpolatedNormal.xyz; + _tangent = interpolatedTangent.xyz; +} diff --git a/libraries/render-utils/src/skin_model_shadow.slv b/libraries/render-utils/src/skin_model_shadow.slv index 6684cfea80..03da2e074e 100644 --- a/libraries/render-utils/src/skin_model_shadow.slv +++ b/libraries/render-utils/src/skin_model_shadow.slv @@ -17,6 +17,7 @@ <$declareStandardTransform()$> <@include Skinning.slh@> +<$declareUseDualQuaternionSkinning()$> void main(void) { vec4 position = vec4(0.0, 0.0, 0.0, 0.0); diff --git a/libraries/render-utils/src/skin_model_shadow_dq.slv b/libraries/render-utils/src/skin_model_shadow_dq.slv new file mode 100644 index 0000000000..74cd4076bc --- /dev/null +++ b/libraries/render-utils/src/skin_model_shadow_dq.slv @@ -0,0 +1,30 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model_shadow.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/24/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include Skinning.slh@> +<$declareUseDualQuaternionSkinning(1)$> + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + skinPosition(inSkinClusterIndex, inSkinClusterWeight, inPosition, position); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, position, gl_Position)$> +} diff --git a/libraries/render-utils/src/skin_model_shadow_fade.slv b/libraries/render-utils/src/skin_model_shadow_fade.slv index 7b27263569..d2e79f9d74 100644 --- a/libraries/render-utils/src/skin_model_shadow_fade.slv +++ b/libraries/render-utils/src/skin_model_shadow_fade.slv @@ -17,6 +17,7 @@ <$declareStandardTransform()$> <@include Skinning.slh@> +<$declareUseDualQuaternionSkinning()$> out vec4 _worldPosition; diff --git a/libraries/render-utils/src/skin_model_shadow_fade_dq.slv b/libraries/render-utils/src/skin_model_shadow_fade_dq.slv new file mode 100644 index 0000000000..fb9c60eefd --- /dev/null +++ b/libraries/render-utils/src/skin_model_shadow_fade_dq.slv @@ -0,0 +1,33 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model_shadow_fade.vert +// vertex shader +// +// Created by Olivier Prat on 06/045/17. +// 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 +// + +<@include gpu/Inputs.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include Skinning.slh@> +<$declareUseDualQuaternionSkinning(1)$> + +out vec4 _worldPosition; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + skinPosition(inSkinClusterIndex, inSkinClusterWeight, inPosition, position); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, position, gl_Position)$> + <$transformModelToWorldPos(obj, position, _worldPosition)$> +} diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index e4dcc7ee03..f6cd6a19e1 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -226,7 +226,6 @@ public: Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } - Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 1dd9f5da49..f175bab99a 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -32,6 +32,7 @@ public: SPECULAR, UNLIT, SKINNED, + DUAL_QUAT_SKINNED, DEPTH_ONLY, DEPTH_BIAS, WIREFRAME, @@ -80,6 +81,7 @@ public: Builder& withSpecular() { _flags.set(SPECULAR); return (*this); } Builder& withUnlit() { _flags.set(UNLIT); return (*this); } Builder& withSkinned() { _flags.set(SKINNED); return (*this); } + Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); return (*this); } Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); return (*this); } Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } @@ -133,6 +135,9 @@ public: Builder& withSkinned() { _flags.set(SKINNED); _mask.set(SKINNED); return (*this); } Builder& withoutSkinned() { _flags.reset(SKINNED); _mask.set(SKINNED); return (*this); } + Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); _mask.set(SKINNED); return (*this); } + Builder& withoutDualQuatSkinned() { _flags.reset(DUAL_QUAT_SKINNED); _mask.set(SKINNED); return (*this); } + Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } Builder& withoutDepthOnly() { _flags.reset(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h index 709c089fdc..af1011a6d8 100644 --- a/libraries/shared/src/DualQuaternion.h +++ b/libraries/shared/src/DualQuaternion.h @@ -55,7 +55,7 @@ protected: inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) { - debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), dual = (" << dq._dual.x << dq._dual.y << dq._dual.z << dq._dual.w << ")"; + debug << "DualQuaternion, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), dual = (" << dq._dual.x << dq._dual.y << dq._dual.z << dq._dual.w << ")"; return debug; } diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index ff1d29eed1..72710a6a7d 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -601,3 +601,6 @@ glm::vec3 randVector() { return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f; } +bool isNonUniformScale(const glm::vec3& scale) { + return fabsf(scale.x - scale.y) > EPSILON || fabsf(scale.y - scale.z) > EPSILON || fabsf(scale.z - scale.x) > EPSILON; +} diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 4f761a4aac..5c9a8b5ca1 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -260,6 +260,8 @@ glm::mat4 orthoInverse(const glm::mat4& m); // Return a random vector of average length 1 glm::vec3 randVector(); +bool isNonUniformScale(const glm::vec3& scale); + // // Safe replacement of glm_mat4_mul() for unaligned arguments instead of __m128 // diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index aa67c14c4b..cb3c0d07b2 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -14,6 +14,10 @@ #include +#ifdef Q_OS_WIN +#include +#endif + #include #include #include @@ -184,8 +188,13 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont } } - QString logMessage = QString("%1 %2").arg(prefixString, message.split('\n').join('\n' + prefixString + " ")); - fprintf(stdout, "%s\n", qPrintable(logMessage)); + QString logMessage = QString("%1 %2\n").arg(prefixString, message.split('\n').join('\n' + prefixString + " ")); + + fprintf(stdout, "%s", qPrintable(logMessage)); +#ifdef Q_OS_WIN + // On windows, this will output log lines into the Visual Studio "output" tab + OutputDebugStringA(qPrintable(logMessage)); +#endif return logMessage; } diff --git a/libraries/shared/src/SettingInterface.cpp b/libraries/shared/src/SettingInterface.cpp index 327668574e..04da35656e 100644 --- a/libraries/shared/src/SettingInterface.cpp +++ b/libraries/shared/src/SettingInterface.cpp @@ -23,51 +23,69 @@ #include "SharedUtil.h" namespace Setting { - static QSharedPointer globalManager; + // This should only run as a post-routine in the QCoreApplication destructor + void cleanupSettingsSaveThread() { + auto globalManager = DependencyManager::get(); + Q_ASSERT(qApp && globalManager); - // cleans up the settings private instance. Should only be run once at closing down. - void cleanupPrivateInstance() { - // grab the thread before we nuke the instance - QThread* settingsManagerThread = DependencyManager::get()->thread(); + // Grab the settings thread to shut it down + QThread* settingsManagerThread = globalManager->thread(); - // tell the private instance to clean itself up on its thread - DependencyManager::destroy(); - - globalManager.reset(); - - // quit the settings manager thread and wait on it to make sure it's gone + // Quit the settings manager thread and wait for it so we + // don't get concurrent accesses when we save all settings below settingsManagerThread->quit(); settingsManagerThread->wait(); + + // [IMPORTANT] Save all settings when the QApplication goes down + globalManager->saveAll(); + + qCDebug(shared) << "Settings thread stopped."; } - void setupPrivateInstance() { - // Ensure Setting::init has already ran and qApp exists - if (qApp && globalManager) { - // Let's set up the settings Private instance on its own thread - QThread* thread = new QThread(); - Q_CHECK_PTR(thread); - thread->setObjectName("Settings Thread"); + // This should only run as a pre-routine in the QCoreApplication constructor + void setupSettingsSaveThread() { + auto globalManager = DependencyManager::get(); + Q_ASSERT(qApp && globalManager); - QObject::connect(thread, SIGNAL(started()), globalManager.data(), SLOT(startTimer())); - QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - QObject::connect(thread, SIGNAL(finished()), globalManager.data(), SLOT(deleteLater())); - globalManager->moveToThread(thread); - thread->start(); - qCDebug(shared) << "Settings thread started."; + // Let's set up the settings private instance on its own thread + QThread* thread = new QThread(qApp); + Q_CHECK_PTR(thread); + thread->setObjectName("Settings Thread"); - // Register cleanupPrivateInstance to run inside QCoreApplication's destructor. - qAddPostRoutine(cleanupPrivateInstance); - } + // Setup setting periodical save timer + QObject::connect(thread, &QThread::started, globalManager.data(), &Manager::startTimer); + QObject::connect(thread, &QThread::finished, globalManager.data(), &Manager::stopTimer); + + // Setup manager threading affinity + // This makes the timer fire on the settings thread so we don't block the main + // thread with a lot of file I/O. + // We bring back the manager to the main thread when the QApplication goes down + globalManager->moveToThread(thread); + QObject::connect(thread, &QThread::finished, globalManager.data(), [] { + auto globalManager = DependencyManager::get(); + Q_ASSERT(qApp && globalManager); + + // Move manager back to the main thread (has to be done on owning thread) + globalManager->moveToThread(qApp->thread()); + }); + + // Start the settings save thread + thread->start(); + qCDebug(shared) << "Settings thread started."; + + // Register cleanupSettingsSaveThread to run inside QCoreApplication's destructor. + // This will cleanup the settings thread and save all settings before shut down. + qAddPostRoutine(cleanupSettingsSaveThread); } - FIXED_Q_COREAPP_STARTUP_FUNCTION(setupPrivateInstance) - // Sets up the settings private instance. Should only be run once at startup. preInit() must be run beforehand, + // Sets up the settings private instance. Should only be run once at startup. void init() { // Set settings format QSettings::setDefaultFormat(JSON_FORMAT); QSettings settings; qCDebug(shared) << "Settings file:" << settings.fileName(); + // Backward compatibility for old settings file if (settings.allKeys().isEmpty()) { loadOldINIFile(settings); } @@ -80,11 +98,13 @@ namespace Setting { qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename; } - globalManager = DependencyManager::set(); + // Setup settings manager, the manager will live until the process shuts down + DependencyManager::set(); - setupPrivateInstance(); + // Add pre-routine to setup threading + qAddPreRoutine(setupSettingsSaveThread); } - + void Interface::init() { if (!DependencyManager::isSet()) { // WARNING: As long as we are using QSettings this should always be triggered for each Setting::Handle diff --git a/libraries/shared/src/SettingManager.cpp b/libraries/shared/src/SettingManager.cpp index 6c246d4cea..2e0850255a 100644 --- a/libraries/shared/src/SettingManager.cpp +++ b/libraries/shared/src/SettingManager.cpp @@ -23,11 +23,7 @@ namespace Setting { // Cleanup timer stopTimer(); delete _saveTimer; - - // Save all settings before exit - saveAll(); - - // sync will be called in the QSettings destructor + _saveTimer = nullptr; } // Custom deleter does nothing, because we need to shutdown later than the dependency manager diff --git a/libraries/shared/src/SettingManager.h b/libraries/shared/src/SettingManager.h index ffdd4ba42a..6696a1ecf4 100644 --- a/libraries/shared/src/SettingManager.h +++ b/libraries/shared/src/SettingManager.h @@ -50,8 +50,8 @@ namespace Setting { QHash _pendingChanges; friend class Interface; - friend void cleanupPrivateInstance(); - friend void setupPrivateInstance(); + friend void cleanupSettingsSaveThread(); + friend void setupSettingsSaveThread(); }; } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 8e5c30711c..412ea59dfe 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -59,16 +59,32 @@ extern "C" FILE * __cdecl __iob_func(void) { #include #include +#include + +#include "LogHandler.h" #include "NumericalConstants.h" #include "OctalCode.h" #include "SharedLogging.h" +// Global instances are stored inside the QApplication properties +// to provide a single instance across DLL boundaries. +// This is something we cannot do here since several DLLs +// and our main binaries statically link this "shared" library +// resulting in multiple static memory blocks in different constexts +// But we need to be able to use global instances before the QApplication +// is setup, so to accomplish that we stage the global instances in a local +// map and setup a pre routine (commitGlobalInstances) that will run in the +// QApplication constructor and commit all the staged instances to the +// QApplication properties. +// Note: One of the side effects of this, is that no DLL loaded before +// the QApplication is constructed, can expect to access the existing staged +// global instanced. For this reason, we advise all DLLs be loaded after +// the QApplication is instanced. +static std::mutex stagedGlobalInstancesMutex; static std::unordered_map stagedGlobalInstances; - std::mutex& globalInstancesMutex() { - static std::mutex mutex; - return mutex; + return stagedGlobalInstancesMutex; } static void commitGlobalInstances() { @@ -78,7 +94,13 @@ static void commitGlobalInstances() { } stagedGlobalInstances.clear(); } -FIXED_Q_COREAPP_STARTUP_FUNCTION(commitGlobalInstances) + +// This call is necessary for global instances to work across DLL boundaries +// Ideally, this founction would be called at the top of the main function. +// See description at the top of the file. +void setupGlobalInstances() { + qAddPreRoutine(commitGlobalInstances); +} QVariant getGlobalInstance(const char* propertyName) { if (qApp) { @@ -813,8 +835,8 @@ bool similarStrings(const QString& stringA, const QString& stringB) { } void disableQtBearerPoll() { - // to disable the Qt constant wireless scanning, set the env for polling interval - qDebug() << "Disabling Qt wireless polling by using a negative value for QTimer::setInterval"; + // To disable the Qt constant wireless scanning, set the env for polling interval to -1 + // The constant polling causes ping spikes on windows every 10 seconds or so that affect the audio const QByteArray DISABLE_BEARER_POLL_TIMEOUT = QString::number(-1).toLocal8Bit(); qputenv("QT_BEARER_POLL_TIMEOUT", DISABLE_BEARER_POLL_TIMEOUT); } @@ -1176,6 +1198,32 @@ void watchParentProcess(int parentPID) { } #endif +void setupHifiApplication(QString applicationName) { + disableQtBearerPoll(); // Fixes wifi ping spikes + + // Those calls are necessary to format the log correctly + // and to direct the application to the correct location + // for read/writes into AppData and other platform equivalents. + QCoreApplication::setApplicationName(applicationName); + QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); + QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); + QCoreApplication::setApplicationVersion(BuildInfo::VERSION); + + // This ensures the global instances mechanism is correctly setup. + // You can find more details as to why this is important in the SharedUtil.h/cpp files + setupGlobalInstances(); + +#ifndef WIN32 + // Windows tends to hold onto log lines until it has a sizeable buffer + // This makes the log feel unresponsive and trap useful log data in the log buffer + // when a crash occurs. + //Force windows to flush the buffer on each new line character to avoid this. + setvbuf(stdout, NULL, _IOLBF, 0); +#endif + + // Install the standard hifi message handler so we get consistant log formatting + qInstallMessageHandler(LogHandler::verboseMessageHandler); +} #ifdef Q_OS_WIN QString getLastErrorAsString() { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 5a1e48d9c0..51a84bed06 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -25,23 +25,6 @@ #include #include -// Workaround for https://bugreports.qt.io/browse/QTBUG-54479 -// Wrap target function inside another function that holds -// a unique string identifier and uses it to ensure it only runs once -// by storing a state within the qApp -// We cannot used std::call_once with a static once_flag because -// this is used in shared libraries that are linked by several DLLs -// (ie. plugins), meaning the static will be useless in that case -#define FIXED_Q_COREAPP_STARTUP_FUNCTION(AFUNC) \ - static void AFUNC ## _fixed() { \ - const auto propertyName = std::string(Q_FUNC_INFO) + __FILE__; \ - if (!qApp->property(propertyName.c_str()).toBool()) { \ - AFUNC(); \ - qApp->setProperty(propertyName.c_str(), QVariant(true)); \ - } \ - } \ - Q_COREAPP_STARTUP_FUNCTION(AFUNC ## _fixed) - // When writing out avatarEntities to a QByteArray, if the parentID is the ID of MyAvatar, use this ID instead. This allows // the value to be reset when the sessionID changes. const QUuid AVATAR_SELF_ID = QUuid("{00000000-0000-0000-0000-000000000001}"); @@ -53,20 +36,11 @@ std::unique_ptr& globalInstancePointer() { return instancePtr; } -template -void setGlobalInstance(const char* propertyName, T* instance) { - globalInstancePointer().reset(instance); -} - -template -bool destroyGlobalInstance() { - std::unique_ptr& instancePtr = globalInstancePointer(); - if (instancePtr.get()) { - instancePtr.reset(); - return true; - } - return false; -} +// Sets up the global instances for use +// This NEEDS to be called on startup +// for any binary planing on using global instances +// More details in cpp file +void setupGlobalInstances(); std::mutex& globalInstancesMutex(); QVariant getGlobalInstance(const char* propertyName); @@ -78,7 +52,6 @@ void setGlobalInstance(const char* propertyName, const QVariant& variant); template T* globalInstance(const char* propertyName, Args&&... args) { static T* resultInstance { nullptr }; - static std::mutex mutex; if (!resultInstance) { std::unique_lock lock(globalInstancesMutex()); if (!resultInstance) { @@ -260,6 +233,7 @@ void watchParentProcess(int parentPID); bool processIsRunning(int64_t pid); +void setupHifiApplication(QString applicationName); #ifdef Q_OS_WIN void* createProcessGroup(); diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 4309789b03..7a938f39c8 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -625,8 +625,7 @@ private: auto windows = qApp->topLevelWindows(); QWindow* result = nullptr; for (auto window : windows) { - QVariant isMainWindow = window->property("MainWindow"); - if (!qobject_cast(window)) { + if (window->objectName().contains("MainWindow")) { result = window; break; } diff --git a/scripts/system/help.js b/scripts/system/help.js index 02d6969718..e29fc59e59 100644 --- a/scripts/system/help.js +++ b/scripts/system/help.js @@ -15,6 +15,7 @@ (function() { // BEGIN LOCAL_SCOPE var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + var HELP_URL = Script.resourcesPath() + "html/tabletHelp.html"; var buttonName = "HELP"; var onHelpScreen = false; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); @@ -39,7 +40,7 @@ } function onScreenChanged(type, url) { - onHelpScreen = type === "Web" && url.startsWith("../../../html/tabletHelp.html"); + onHelpScreen = type === "Web" && url.startsWith(HELP_URL); button.editProperties({ isActive: onHelpScreen }); } diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index c53a2fa5bd..d6710238fb 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -42,25 +42,28 @@ -
+
+ +
@@ -293,7 +296,6 @@
-
BehaviorM @@ -365,8 +367,6 @@
- -
LightM @@ -400,7 +400,6 @@
-
ModelM @@ -484,7 +483,6 @@
-
ZoneM @@ -532,6 +530,10 @@
+
+ + +
Skybox @@ -676,7 +678,7 @@ min="-1000" max="50000" step="10"> -
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 2b29fbf041..4db9bd00c6 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -616,6 +616,8 @@ function loaded() { var elShape = document.getElementById("property-shape"); + var elCanCastShadow = document.getElementById("property-can-cast-shadow"); + var elLightSpotLight = document.getElementById("property-light-spot-light"); var elLightColor = document.getElementById("property-light-color"); var elLightColorRed = document.getElementById("property-light-color-red"); @@ -687,6 +689,8 @@ function loaded() { var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x"); var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); + var elZoneKeyLightCastShadows = document.getElementById("property-zone-key-light-cast-shadows"); + // Skybox var elZoneSkyboxModeInherit = document.getElementById("property-zone-skybox-mode-inherit"); var elZoneSkyboxModeDisabled = document.getElementById("property-zone-skybox-mode-disabled"); @@ -844,7 +848,6 @@ function loaded() { elLocked.checked = properties.locked; - elName.value = properties.name; elVisible.checked = properties.visible; @@ -1011,6 +1014,12 @@ function loaded() { properties.color.green + "," + properties.color.blue + ")"; } + if (properties.type === "Model" || + properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") { + + elCanCastShadow.checked = properties.canCastShadow; + } + if (properties.type === "Model") { elModelURL.value = properties.modelURL; elShapeType.value = properties.shapeType; @@ -1060,7 +1069,6 @@ function loaded() { elLightFalloffRadius.value = properties.falloffRadius.toFixed(1); elLightExponent.value = properties.exponent.toFixed(2); elLightCutoff.value = properties.cutoff.toFixed(2); - } else if (properties.type === "Zone") { // Key light elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); @@ -1076,6 +1084,8 @@ function loaded() { elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2); elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); + elZoneKeyLightCastShadows.checked = properties.keyLight.castShadows; + // Skybox elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled'); @@ -1139,13 +1149,15 @@ function loaded() { // Show/hide sections as required showElements(document.getElementsByClassName('skybox-section'), elZoneSkyboxModeEnabled.checked); + showElements(document.getElementsByClassName('keylight-section'), elZoneKeyLightModeEnabled.checked); + showElements(document.getElementsByClassName('ambient-section'), elZoneAmbientLightModeEnabled.checked); + showElements(document.getElementsByClassName('haze-section'), elZoneHazeModeEnabled.checked); - } else if (properties.type === "PolyVox") { elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); @@ -1176,6 +1188,15 @@ function loaded() { elMaterialMappingRot.value = properties.materialMappingRot.toFixed(2); } + // Only these types can cast a shadow + if (properties.type === "Model" || + properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") { + + showElements(document.getElementsByClassName('can-cast-shadow-section'), true); + } else { + showElements(document.getElementsByClassName('can-cast-shadow-section'), false); + } + if (properties.locked) { disableProperties(); elLocked.removeAttribute('disabled'); @@ -1432,6 +1453,8 @@ function loaded() { elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); + elCanCastShadow.addEventListener('change', createEmitCheckedPropertyUpdateFunction('canCastShadow')); + elImageURL.addEventListener('change', createImageURLUpdateFunction('textures')); elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); @@ -1590,6 +1613,9 @@ function loaded() { elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); + elZoneKeyLightCastShadows.addEventListener('change', + createEmitGroupCheckedPropertyUpdateFunction('keyLight', 'castShadows')); + // Skybox var skyboxModeChanged = createZoneComponentModeChangedFunction('skyboxMode', elZoneSkyboxModeInherit, elZoneSkyboxModeDisabled, elZoneSkyboxModeEnabled); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 631b5e97ac..8f51d88f2d 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -551,11 +551,7 @@ var selectionDisplay = null; // for gridTool.js to ignore break; case 'checkout_rezClicked': case 'purchases_rezClicked': - if (message.itemType === "app") { - console.log("How did you get here? You can't buy apps yet!"); - } else { - rezEntity(message.itemHref, message.itemType); - } + rezEntity(message.itemHref, message.itemType); break; case 'header_marketplaceImageClicked': case 'purchases_backClicked': diff --git a/tests/entities/src/main.cpp b/tests/entities/src/main.cpp index 792ef7d9c6..bf79f9d3e9 100644 --- a/tests/entities/src/main.cpp +++ b/tests/entities/src/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include const QString& getTestResourceDir() { static QString dir; @@ -136,6 +137,8 @@ void testPropertyFlags() { } int main(int argc, char** argv) { + setupHifiApplication("Entities Test"); + QCoreApplication app(argc, argv); { auto start = usecTimestampNow(); diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 549baddbc3..41d84ca026 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -192,7 +193,9 @@ void testSparseRectify() { } } -int main(int argc, char** argv) { +int main(int argc, char** argv) { + setupHifiApplication("GPU Test"); + testSparseRectify(); // FIXME this test appears to be broken diff --git a/tests/ktx/src/KtxTests.cpp b/tests/ktx/src/KtxTests.cpp index 94e5d7e8e7..65d9cbec3d 100644 --- a/tests/ktx/src/KtxTests.cpp +++ b/tests/ktx/src/KtxTests.cpp @@ -149,7 +149,8 @@ static const QString TEST_FOLDER { "H:/ktx_cacheold" }; static const QString EXTENSIONS { "*.ktx" }; int mainTemp(int, char**) { - qInstallMessageHandler(messageHandler); + setupHifiApplication("KTX Tests"); + auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS }); for (auto fileInfo : fileInfoList) { qDebug() << fileInfo.filePath(); diff --git a/tests/qml/src/main.cpp b/tests/qml/src/main.cpp index 219efa5996..022f7290f4 100644 --- a/tests/qml/src/main.cpp +++ b/tests/qml/src/main.cpp @@ -179,18 +179,10 @@ void TestWindow::resizeEvent(QResizeEvent* ev) { resizeWindow(ev->size()); } -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (!message.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(message.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#endif - } -} +int main(int argc, char** argv) { + setupHifiApplication("QML Test"); -int main(int argc, char** argv) { QGuiApplication app(argc, argv); - qInstallMessageHandler(messageHandler); TestWindow window; app.exec(); return 0; diff --git a/tests/qt59/src/main.cpp b/tests/qt59/src/main.cpp index 7b95cabd6c..19b922de9f 100644 --- a/tests/qt59/src/main.cpp +++ b/tests/qt59/src/main.cpp @@ -63,14 +63,11 @@ void Qt59TestApp::finish(int exitCode) { int main(int argc, char * argv[]) { - QCoreApplication::setApplicationName("Qt59Test"); - QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); - QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); - QCoreApplication::setApplicationVersion(BuildInfo::VERSION); + setupHifiApplication("Qt59Test"); Qt59TestApp app(argc, argv); return app.exec(); } -#include "main.moc" \ No newline at end of file +#include "main.moc" diff --git a/tests/recording/src/main.cpp b/tests/recording/src/main.cpp index f4049b04b7..1b4e5adf6d 100644 --- a/tests/recording/src/main.cpp +++ b/tests/recording/src/main.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "Constants.h" using namespace recording; @@ -97,18 +99,10 @@ void testClipOrdering() { Q_UNUSED(lastFrameTimeOffset); // FIXME - Unix build not yet upgraded to Qt 5.5.1 we can remove this once it is } -#ifdef Q_OS_WIN32 -void myMessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg) { - OutputDebugStringA(msg.toLocal8Bit().toStdString().c_str()); - OutputDebugStringA("\n"); -} -#endif - int main(int, const char**) { -#ifdef Q_OS_WIN32 - qInstallMessageHandler(myMessageHandler); -#endif + setupHifiApplication("Recording Test"); + testFrameTypeRegistration(); testFilePersist(); testClipOrdering(); -} \ No newline at end of file +} diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 2e058d95fc..93672cc5a2 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -1138,31 +1138,17 @@ private: bool QTestWindow::_cullingEnabled = true; -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); - - if (!logMessage.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(logMessage.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#endif - logger->addMessage(qPrintable(logMessage + "\n")); - } -} - const char * LOG_FILTER_RULES = R"V0G0N( hifi.gpu=true )V0G0N"; int main(int argc, char** argv) { + setupHifiApplication("RenderPerf"); + QApplication app(argc, argv); - QCoreApplication::setApplicationName("RenderPerf"); - QCoreApplication::setOrganizationName("High Fidelity"); - QCoreApplication::setOrganizationDomain("highfidelity.com"); logger.reset(new FileLogger()); - qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow::setup(); QTestWindow window; diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index 8db66b9288..ce666065e3 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -610,16 +610,6 @@ private: bool _ready { false }; }; -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (!message.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(message.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#endif - std::cout << message.toLocal8Bit().constData() << std::endl; - } -} - const char * LOG_FILTER_RULES = R"V0G0N( hifi.gpu=true )V0G0N"; @@ -645,11 +635,9 @@ void unzipTestData(const QByteArray& zipData) { } int main(int argc, char** argv) { + setupHifiApplication("RenderPerf"); + QApplication app(argc, argv); - QCoreApplication::setApplicationName("RenderPerf"); - QCoreApplication::setOrganizationName("High Fidelity"); - QCoreApplication::setOrganizationDomain("highfidelity.com"); - qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); if (!DATA_DIR.exists()) { diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 741fdbdddd..e30a80f3d9 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -179,25 +179,14 @@ void QTestWindow::draw() { } } -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (!message.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(message.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#else - std::cout << message.toLocal8Bit().constData() << std::endl; -#endif - } -} - - const char * LOG_FILTER_RULES = R"V0G0N( hifi.gpu=true )V0G0N"; -int main(int argc, char** argv) { +int main(int argc, char** argv) { + setupHifiApplication("Render Utils Test"); + QGuiApplication app(argc, argv); - qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow window; QTimer timer; diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 585bc432a9..a3b4196031 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -214,24 +214,14 @@ void QTestWindow::draw() { _context.swapBuffers(this); } -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (!message.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(message.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#else - std::cout << message.toLocal8Bit().constData() << std::endl; -#endif - } -} - const char * LOG_FILTER_RULES = R"V0G0N( hifi.gpu=true )V0G0N"; -int main(int argc, char** argv) { +int main(int argc, char** argv) { + setupHifiApplication("Shaders Test"); + QGuiApplication app(argc, argv); - qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow window; QTimer timer; diff --git a/tools/ac-client/src/main.cpp b/tools/ac-client/src/main.cpp index c9affde3b5..024a891d3c 100644 --- a/tools/ac-client/src/main.cpp +++ b/tools/ac-client/src/main.cpp @@ -19,15 +19,11 @@ using namespace std; -int main(int argc, char * argv[]) { - QCoreApplication::setApplicationName(BuildInfo::AC_CLIENT_SERVER_NAME); - QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); - QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); - QCoreApplication::setApplicationVersion(BuildInfo::VERSION); - +int main(int argc, char* argv[]) { + setupHifiApplication(BuildInfo::AC_CLIENT_SERVER_NAME); + Setting::init(); ACClientApp app(argc, argv); - return app.exec(); } diff --git a/tools/atp-client/src/main.cpp b/tools/atp-client/src/main.cpp index 830c049bc7..2a267c088c 100644 --- a/tools/atp-client/src/main.cpp +++ b/tools/atp-client/src/main.cpp @@ -20,14 +20,10 @@ using namespace std; int main(int argc, char * argv[]) { - QCoreApplication::setApplicationName(BuildInfo::AC_CLIENT_SERVER_NAME); - QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION); - QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN); - QCoreApplication::setApplicationVersion(BuildInfo::VERSION); - + setupHifiApplication("ATP Client"); + Setting::init(); ATPClientApp app(argc, argv); - return app.exec(); } diff --git a/tools/auto-tester/src/main.cpp b/tools/auto-tester/src/main.cpp index 45a3743482..6e5e06b732 100644 --- a/tools/auto-tester/src/main.cpp +++ b/tools/auto-tester/src/main.cpp @@ -17,4 +17,4 @@ int main(int argc, char *argv[]) { autoTester.show(); return application.exec(); -} \ No newline at end of file +} diff --git a/tools/ice-client/src/main.cpp b/tools/ice-client/src/main.cpp index c70a7eb7d7..60a5d4e0e4 100644 --- a/tools/ice-client/src/main.cpp +++ b/tools/ice-client/src/main.cpp @@ -13,11 +13,15 @@ #include #include +#include + #include "ICEClientApp.h" using namespace std; int main(int argc, char * argv[]) { + setupHifiApplication("ICE Client"); + ICEClientApp app(argc, argv); return app.exec(); } diff --git a/tools/oven/src/main.cpp b/tools/oven/src/main.cpp index 788470b75e..3f4afe1f15 100644 --- a/tools/oven/src/main.cpp +++ b/tools/oven/src/main.cpp @@ -10,11 +10,12 @@ #include "Oven.h" +#include #include +#include int main (int argc, char** argv) { - QCoreApplication::setOrganizationName("High Fidelity"); - QCoreApplication::setApplicationName("Oven"); + setupHifiApplication("Oven"); // init the settings interface so we can save and load settings Setting::init(); diff --git a/tools/skeleton-dump/src/main.cpp b/tools/skeleton-dump/src/main.cpp index 6cf4d41f31..d5919c4a88 100644 --- a/tools/skeleton-dump/src/main.cpp +++ b/tools/skeleton-dump/src/main.cpp @@ -14,9 +14,13 @@ #include #include +#include + #include "SkeletonDumpApp.h" int main(int argc, char * argv[]) { + setupHifiApplication("Skeleton Dump App"); + SkeletonDumpApp app(argc, argv); return app.getReturnCode(); } diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp index 6161dbfdbc..ce89f04ce5 100644 --- a/tools/udt-test/src/UDTTest.cpp +++ b/tools/udt-test/src/UDTTest.cpp @@ -71,8 +71,6 @@ const QStringList SERVER_STATS_TABLE_HEADERS { UDTTest::UDTTest(int& argc, char** argv) : QCoreApplication(argc, argv) { - qInstallMessageHandler(LogHandler::verboseMessageHandler); - parseArguments(); // randomize the seed for packet size randomization diff --git a/tools/udt-test/src/main.cpp b/tools/udt-test/src/main.cpp index ccb7d0af0f..d88218c0d0 100644 --- a/tools/udt-test/src/main.cpp +++ b/tools/udt-test/src/main.cpp @@ -10,9 +10,13 @@ #include +#include + #include "UDTTest.h" int main(int argc, char* argv[]) { + setupHifiApplication("UDT Test); + UDTTest app(argc, argv); return app.exec(); } diff --git a/tools/vhacd-util/src/main.cpp b/tools/vhacd-util/src/main.cpp index 42c9db9513..817e77bf8e 100644 --- a/tools/vhacd-util/src/main.cpp +++ b/tools/vhacd-util/src/main.cpp @@ -15,6 +15,8 @@ #include #include +#include + #include "VHACDUtilApp.h" using namespace std; @@ -22,6 +24,8 @@ using namespace VHACD; int main(int argc, char * argv[]) { + setupHifiApplication("VHACD Util"); + VHACDUtilApp app(argc, argv); return app.getReturnCode(); }