# Conflicts: # interface/src/Application.cpp # interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp
Showing the changes to interface/src/Application.cpp (1 of 1575 files). Show all
Unified · Split
| @@ -144,6 +144,7 @@ · 143 unmodified lines | ||
| 144 | 144 | #include <trackers/EyeTracker.h> |
| 145 | 145 | #include <avatars-renderer/ScriptAvatar.h> |
| 146 | 146 | #include <RenderableEntityItem.h> |
| 147 | +#include <procedural/ProceduralSkybox.h> | |
| 147 | 148 | |
| 148 | 149 | #include "AudioClient.h" |
| 149 | 150 | #include "audio/AudioScope.h" |
| @@ -218,14 +219,16 @@ · 68 unmodified lines | ||
| 218 | 219 | #include "webbrowser/WebBrowserSuggestionsEngine.h" |
| 219 | 220 | #include <DesktopPreviewProvider.h> |
| 220 | 221 | |
| 222 | +#include "AboutUtil.h" | |
| 223 | + | |
| 221 | 224 | #if defined(Q_OS_WIN) |
| 222 | 225 | #include <VersionHelpers.h> |
| 223 | 226 | |
| 224 | 227 | #ifdef DEBUG_EVENT_QUEUE |
| 225 | 228 | // This is a HACK that uses private headers included with the qt source distrubution. |
| 226 | 229 | // To use this feature you need to add these directores to your include path: |
| 227 | −// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1/QtCore | |
| 228 | −// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1 | |
| 230 | +// E:/Qt/5.10.1/Src/qtbase/include/QtCore/5.10.1/QtCore | |
| 231 | +// E:/Qt/5.10.1/Src/qtbase/include/QtCore/5.10.1 | |
| 229 | 232 | #define QT_BOOTSTRAPPED |
| 230 | 233 | #include <private/qthread_p.h> |
| 231 | 234 | #include <private/qobject_p.h> |
| @@ -241,6 +244,7 @@ extern "C" { · 9 unmodified lines | ||
| 241 | 244 | |
| 242 | 245 | #if defined(Q_OS_ANDROID) |
| 243 | 246 | #include <android/log.h> |
| 247 | +#include "AndroidHelper.h" | |
| 244 | 248 | #endif |
| 245 | 249 | |
| 246 | 250 | enum ApplicationEvent { |
| @@ -329,7 +333,11 @@ static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" }; · 82 unmodified lines | ||
| 329 | 333 | static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); |
| 330 | 334 | #endif |
| 331 | 335 | |
| 336 | +#if !defined(Q_OS_ANDROID) | |
| 332 | 337 | static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; |
| 338 | +#else | |
| 339 | +static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 4; | |
| 340 | +#endif | |
| 333 | 341 | |
| 334 | 342 | // For processing on QThreadPool, we target a number of threads after reserving some |
| 335 | 343 | // based on how many are being consumed by the application and the display plugin. However, |
| @@ -373,7 +381,7 @@ Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTRE · 37 unmodified lines | ||
| 373 | 381 | static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com"; |
| 374 | 382 | static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds |
| 375 | 383 | static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; |
| 376 | − | |
| 384 | +static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin"; | |
| 377 | 385 | static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; |
| 378 | 386 | |
| 379 | 387 | const std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions { |
| @@ -489,7 +497,7 @@ public: · 109 unmodified lines | ||
| 489 | 497 | // Don't actually crash in debug builds, in case this apparent deadlock is simply from |
| 490 | 498 | // the developer actively debugging code |
| 491 | 499 | #ifdef NDEBUG |
| 492 | − deadlockDetectionCrash(); | |
| 500 | + deadlockDetectionCrash(); | |
| 493 | 501 | #endif |
| 494 | 502 | } |
| 495 | 503 | } |
| @@ -687,6 +695,34 @@ private: · 191 unmodified lines | ||
| 687 | 695 | } |
| 688 | 696 | }; |
| 689 | 697 | |
| 698 | +/**jsdoc | |
| 699 | + * <p>The <code>Controller.Hardware.Application</code> object has properties representing Interface's state. The property | |
| 700 | + * values are integer IDs, uniquely identifying each output. <em>Read-only.</em> These can be mapped to actions or functions or | |
| 701 | + * <code>Controller.Standard</code> items in a {@link RouteObject} mapping (e.g., using the {@link RouteObject#when} method). | |
| 702 | + * Each data value is either <code>1.0</code> for "true" or <code>0.0</code> for "false".</p> | |
| 703 | + * <table> | |
| 704 | + * <thead> | |
| 705 | + * <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr> | |
| 706 | + * </thead> | |
| 707 | + * <tbody> | |
| 708 | + * <tr><td><code>CameraFirstPerson</code></td><td>number</td><td>number</td><td>The camera is in first-person mode. | |
| 709 | + * </td></tr> | |
| 710 | + * <tr><td><code>CameraThirdPerson</code></td><td>number</td><td>number</td><td>The camera is in third-person mode. | |
| 711 | + * </td></tr> | |
| 712 | + * <tr><td><code>CameraFSM</code></td><td>number</td><td>number</td><td>The camera is in full screen mirror mode.</td></tr> | |
| 713 | + * <tr><td><code>CameraIndependent</code></td><td>number</td><td>number</td><td>The camera is in independent mode.</td></tr> | |
| 714 | + * <tr><td><code>CameraEntity</code></td><td>number</td><td>number</td><td>The camera is in entity mode.</td></tr> | |
| 715 | + * <tr><td><code>InHMD</code></td><td>number</td><td>number</td><td>The user is in HMD mode.</td></tr> | |
| 716 | + * <tr><td><code>AdvancedMovement</code></td><td>number</td><td>number</td><td>Advanced movement controls are enabled. | |
| 717 | + * </td></tr> | |
| 718 | + * <tr><td><code>SnapTurn</code></td><td>number</td><td>number</td><td>Snap turn is enabled.</td></tr> | |
| 719 | + * <tr><td><code>Grounded</code></td><td>number</td><td>number</td><td>The user's avatar is on the ground.</td></tr> | |
| 720 | + * <tr><td><code>NavigationFocused</code></td><td>number</td><td>number</td><td><em>Not used.</em></td></tr> | |
| 721 | + * </tbody> | |
| 722 | + * </table> | |
| 723 | + * @typedef {object} Controller.Hardware-Application | |
| 724 | + */ | |
| 725 | + | |
| 690 | 726 | static const QString STATE_IN_HMD = "InHMD"; |
| 691 | 727 | static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM"; |
| 692 | 728 | static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson"; |
| @@ -703,6 +739,11 @@ extern DisplayPluginList getDisplayPlugins(); · 10 unmodified lines | ||
| 703 | 739 | extern InputPluginList getInputPlugins(); |
| 704 | 740 | extern void saveInputPluginSettings(const InputPluginList& plugins); |
| 705 | 741 | |
| 742 | +// Parameters used for running tests from teh command line | |
| 743 | +const QString TEST_SCRIPT_COMMAND{ "--testScript" }; | |
| 744 | +const QString TEST_QUIT_WHEN_FINISHED_OPTION{ "quitWhenFinished" }; | |
| 745 | +const QString TEST_RESULTS_LOCATION_COMMAND{ "--testResultsLocation" }; | |
| 746 | + | |
| 706 | 747 | bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { |
| 707 | 748 | const char** constArgv = const_cast<const char**>(argv); |
| 708 | 749 | |
| @@ -737,7 +778,22 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 28 unmodified lines | ||
| 737 | 778 | |
| 738 | 779 | static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset"; |
| 739 | 780 | bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET); |
| 740 | − bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt); | |
| 781 | + | |
| 782 | + // Ignore any previous crashes if running from command line with a test script. | |
| 783 | + bool inTestMode { false }; | |
| 784 | + for (int i = 0; i < argc; ++i) { | |
| 785 | + QString parameter(argv[i]); | |
| 786 | + if (parameter == TEST_SCRIPT_COMMAND) { | |
| 787 | + inTestMode = true; | |
| 788 | + break; | |
| 789 | + } | |
| 790 | + } | |
| 791 | + | |
| 792 | + bool previousSessionCrashed { false }; | |
| 793 | + if (!inTestMode) { | |
| 794 | + previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt); | |
| 795 | + } | |
| 796 | + | |
| 741 | 797 | // get dir to use for cache |
| 742 | 798 | static const auto CACHE_SWITCH = "--cache"; |
| 743 | 799 | QString cacheDir = getCmdOption(argc, const_cast<const char**>(argv), CACHE_SWITCH); |
| @@ -745,15 +801,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 1 unmodified line | ||
| 745 | 801 | qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir); |
| 746 | 802 | } |
| 747 | 803 | |
| 748 | − // FIXME fix the OSX installer to install the resources.rcc binary instead of resource files and remove | |
| 749 | − // this conditional exclusion | |
| 750 | −#if !defined(Q_OS_OSX) | |
| 751 | 804 | { |
| 752 | −#if defined(Q_OS_ANDROID) | |
| 753 | − const QString resourcesBinaryFile = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources.rcc"; | |
| 754 | −#else | |
| 755 | − const QString resourcesBinaryFile = QCoreApplication::applicationDirPath() + "/resources.rcc"; | |
| 756 | −#endif | |
| 805 | + const QString resourcesBinaryFile = PathUtils::getRccPath(); | |
| 757 | 806 | if (!QFile::exists(resourcesBinaryFile)) { |
| 758 | 807 | throw std::runtime_error("Unable to find primary resources"); |
| 759 | 808 | } |
| @@ -761,7 +810,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 1 unmodified line | ||
| 761 | 810 | throw std::runtime_error("Unable to load primary resources"); |
| 762 | 811 | } |
| 763 | 812 | } |
| 764 | −#endif | |
| 765 | 813 | |
| 766 | 814 | // Tell the plugin manager about our statically linked plugins |
| 767 | 815 | auto pluginManager = PluginManager::getInstance(); |
| @@ -772,7 +820,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 4 unmodified lines | ||
| 772 | 820 | steamClient->init(); |
| 773 | 821 | } |
| 774 | 822 | |
| 775 | − DependencyManager::set<tracing::Tracer>(); | |
| 776 | 823 | PROFILE_SET_THREAD_NAME("Main Thread"); |
| 777 | 824 | |
| 778 | 825 | #if defined(Q_OS_WIN) |
| @@ -801,7 +848,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 22 unmodified lines | ||
| 801 | 848 | DependencyManager::set<Cursor::Manager>(); |
| 802 | 849 | DependencyManager::set<VirtualPad::Manager>(); |
| 803 | 850 | DependencyManager::set<DesktopPreviewProvider>(); |
| 851 | +#if defined(Q_OS_ANDROID) | |
| 852 | + DependencyManager::set<AccountManager>(); // use the default user agent getter | |
| 853 | +#else | |
| 804 | 854 | DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp)); |
| 855 | +#endif | |
| 805 | 856 | DependencyManager::set<StatTracker>(); |
| 806 | 857 | DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT); |
| 807 | 858 | DependencyManager::set<ScriptInitializerMixin, NativeScriptInitializers>(); |
| @@ -853,7 +904,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { · 45 unmodified lines | ||
| 853 | 904 | DependencyManager::set<DiscoverabilityManager>(); |
| 854 | 905 | DependencyManager::set<SceneScriptingInterface>(); |
| 855 | 906 | DependencyManager::set<OffscreenUi>(); |
| 856 | − DependencyManager::set<AutoUpdater>(); | |
| 857 | 907 | DependencyManager::set<Midi>(); |
| 858 | 908 | DependencyManager::set<PathUtils>(); |
| 859 | 909 | DependencyManager::set<InterfaceDynamicFactory>(); |
| @@ -927,7 +977,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 67 unmodified lines | ||
| 927 | 977 | _entitySimulation(new PhysicalEntitySimulation()), |
| 928 | 978 | _physicsEngine(new PhysicsEngine(Vectors::ZERO)), |
| 929 | 979 | _entityClipboard(new EntityTree()), |
| 930 | − _lastQueriedTime(usecTimestampNow()), | |
| 931 | 980 | _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), |
| 932 | 981 | _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), |
| 933 | 982 | _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), |
| @@ -956,21 +1005,35 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 22 unmodified lines | ||
| 956 | 1005 | auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); |
| 957 | 1006 | setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); |
| 958 | 1007 | setProperty(hifi::properties::CRASHED, _previousSessionCrashed); |
| 959 | − | |
| 960 | 1008 | { |
| 961 | − const QString TEST_SCRIPT = "--testScript"; | |
| 962 | − const QString TRACE_FILE = "--traceFile"; | |
| 963 | 1009 | const QStringList args = arguments(); |
| 1010 | + | |
| 964 | 1011 | for (int i = 0; i < args.size() - 1; ++i) { |
| 965 | − if (args.at(i) == TEST_SCRIPT) { | |
| 1012 | + if (args.at(i) == TEST_SCRIPT_COMMAND && (i + 1) < args.size()) { | |
| 966 | 1013 | QString testScriptPath = args.at(i + 1); |
| 967 | − if (QFileInfo(testScriptPath).exists()) { | |
| 1014 | + | |
| 1015 | + // If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file | |
| 1016 | + // This is done so as not break previous command line scripts | |
| 1017 | + if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || | |
| 1018 | + testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) { | |
| 1019 | + | |
| 1020 | + setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath)); | |
| 1021 | + } else if (QFileInfo(testScriptPath).exists()) { | |
| 968 | 1022 | setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); |
| 969 | 1023 | } |
| 970 | − } else if (args.at(i) == TRACE_FILE) { | |
| 971 | − QString traceFilePath = args.at(i + 1); | |
| 972 | − setProperty(hifi::properties::TRACING, traceFilePath); | |
| 973 | − DependencyManager::get<tracing::Tracer>()->startTracing(); | |
| 1024 | + | |
| 1025 | + // quite when finished parameter must directly follow the test script | |
| 1026 | + if ((i + 2) < args.size() && args.at(i + 2) == TEST_QUIT_WHEN_FINISHED_OPTION) { | |
| 1027 | + quitWhenFinished = true; | |
| 1028 | + } | |
| 1029 | + } else if (args.at(i) == TEST_RESULTS_LOCATION_COMMAND) { | |
| 1030 | + // Set test snapshot location only if it is a writeable directory | |
| 1031 | + QString path(args.at(i + 1)); | |
| 1032 | + | |
| 1033 | + QFileInfo fileInfo(path); | |
| 1034 | + if (fileInfo.isDir() && fileInfo.isWritable()) { | |
| 1035 | + TestScriptingInterface::getInstance()->setTestResultsLocation(path); | |
| 1036 | + } | |
| 974 | 1037 | } |
| 975 | 1038 | } |
| 976 | 1039 | } |
| @@ -1016,6 +1079,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 39 unmodified lines | ||
| 1016 | 1079 | auto nodeList = DependencyManager::get<NodeList>(); |
| 1017 | 1080 | nodeList->startThread(); |
| 1018 | 1081 | |
| 1082 | + const char** constArgv = const_cast<const char**>(argv); | |
| 1083 | + if (cmdOptionExists(argc, constArgv, "--disableWatchdog")) { | |
| 1084 | + DISABLE_WATCHDOG = true; | |
| 1085 | + } | |
| 1019 | 1086 | // Set up a watchdog thread to intentionally crash the application on deadlocks |
| 1020 | 1087 | if (!DISABLE_WATCHDOG) { |
| 1021 | 1088 | (new DeadlockWatchdogThread())->start(); |
| @@ -1033,10 +1100,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 11 unmodified lines | ||
| 1033 | 1100 | if (steamClient) { |
| 1034 | 1101 | qCDebug(interfaceapp) << "[VERSION] SteamVR buildID:" << steamClient->getSteamVRBuildID(); |
| 1035 | 1102 | } |
| 1103 | + setCrashAnnotation("steam", property(hifi::properties::STEAM).toBool() ? "1" : "0"); | |
| 1104 | + | |
| 1036 | 1105 | qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion()); |
| 1037 | 1106 | qCDebug(interfaceapp) << "[VERSION] MODIFIED_ORGANIZATION:" << BuildInfo::MODIFIED_ORGANIZATION; |
| 1038 | 1107 | qCDebug(interfaceapp) << "[VERSION] VERSION:" << BuildInfo::VERSION; |
| 1039 | − qCDebug(interfaceapp) << "[VERSION] BUILD_BRANCH:" << BuildInfo::BUILD_BRANCH; | |
| 1108 | + qCDebug(interfaceapp) << "[VERSION] BUILD_TYPE_STRING:" << BuildInfo::BUILD_TYPE_STRING; | |
| 1040 | 1109 | qCDebug(interfaceapp) << "[VERSION] BUILD_GLOBAL_SERVICES:" << BuildInfo::BUILD_GLOBAL_SERVICES; |
| 1041 | 1110 | #if USE_STABLE_GLOBAL_SERVICES |
| 1042 | 1111 | qCDebug(interfaceapp) << "[VERSION] We will use STABLE global services."; |
| @@ -1100,10 +1169,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 57 unmodified lines | ||
| 1100 | 1169 | auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>(); |
| 1101 | 1170 | auto myAvatarPosition = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldPosition(); |
| 1102 | 1171 | float distance = glm::distance(myAvatarPosition, position); |
| 1103 | − bool shouldMute = !audioClient->isMuted() && (distance < radius); | |
| 1104 | 1172 | |
| 1105 | − if (shouldMute) { | |
| 1106 | − audioClient->toggleMute(); | |
| 1173 | + if (distance < radius) { | |
| 1174 | + audioClient->setMuted(true); | |
| 1107 | 1175 | audioScriptingInterface->environmentMuted(); |
| 1108 | 1176 | } |
| 1109 | 1177 | }); |
| @@ -1119,6 +1187,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 9 unmodified lines | ||
| 1119 | 1187 | const DomainHandler& domainHandler = nodeList->getDomainHandler(); |
| 1120 | 1188 | |
| 1121 | 1189 | connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl))); |
| 1190 | + connect(&domainHandler, &DomainHandler::domainURLChanged, [](QUrl domainURL){ | |
| 1191 | + setCrashAnnotation("domain", domainURL.toString().toStdString()); | |
| 1192 | + }); | |
| 1122 | 1193 | connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); |
| 1123 | 1194 | connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle())); |
| 1124 | 1195 | connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); |
| @@ -1162,8 +1233,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 37 unmodified lines | ||
| 1162 | 1233 | nodeList.data(), SLOT(reset())); |
| 1163 | 1234 | |
| 1164 | 1235 | auto dialogsManager = DependencyManager::get<DialogsManager>(); |
| 1236 | +#if defined(Q_OS_ANDROID) | |
| 1237 | + connect(accountManager.data(), &AccountManager::authRequired, this, []() { | |
| 1238 | + AndroidHelper::instance().showLoginDialog(); | |
| 1239 | + }); | |
| 1240 | +#else | |
| 1165 | 1241 | connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog); |
| 1242 | +#endif | |
| 1166 | 1243 | connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle); |
| 1244 | + connect(accountManager.data(), &AccountManager::usernameChanged, [](QString username){ | |
| 1245 | + setCrashAnnotation("username", username.toStdString()); | |
| 1246 | + }); | |
| 1167 | 1247 | |
| 1168 | 1248 | // set the account manager's root URL and trigger a login request if we don't have the access token |
| 1169 | 1249 | accountManager->setIsAgent(true); |
| @@ -1181,6 +1261,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 11 unmodified lines | ||
| 1181 | 1261 | connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateThreadPoolCount); |
| 1182 | 1262 | connect(this, &Application::activeDisplayPluginChanged, this, [](){ |
| 1183 | 1263 | qApp->setProperty(hifi::properties::HMD, qApp->isHMDMode()); |
| 1264 | + auto displayPlugin = qApp->getActiveDisplayPlugin(); | |
| 1265 | + setCrashAnnotation("display_plugin", displayPlugin->getName().toStdString()); | |
| 1266 | + setCrashAnnotation("hmd", displayPlugin->isHmd() ? "1" : "0"); | |
| 1184 | 1267 | }); |
| 1185 | 1268 | connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateSystemTabletMode); |
| 1186 | 1269 | |
| @@ -1188,6 +1271,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 1 unmodified line | ||
| 1188 | 1271 | connect(myAvatar.get(), &MyAvatar::positionGoneTo, |
| 1189 | 1272 | DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); |
| 1190 | 1273 | |
| 1274 | + connect(myAvatar.get(), &MyAvatar::skeletonModelURLChanged, [](){ | |
| 1275 | + QUrl avatarURL = qApp->getMyAvatar()->getSkeletonModelURL(); | |
| 1276 | + setCrashAnnotation("avatar", avatarURL.toString().toStdString()); | |
| 1277 | + }); | |
| 1278 | + | |
| 1279 | + | |
| 1191 | 1280 | // Inititalize sample before registering |
| 1192 | 1281 | _sampleSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/sample.wav")); |
| 1193 | 1282 | |
| @@ -1205,6 +1294,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 11 unmodified lines | ||
| 1205 | 1294 | |
| 1206 | 1295 | connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] { |
| 1207 | 1296 | getEntities()->reloadEntityScripts(); |
| 1297 | + loadAvatarScripts(getMyAvatar()->getScriptUrls()); | |
| 1208 | 1298 | }, Qt::QueuedConnection); |
| 1209 | 1299 | |
| 1210 | 1300 | connect(scriptEngines, &ScriptEngines::scriptLoadError, |
| @@ -1225,7 +1315,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 14 unmodified lines | ||
| 1225 | 1315 | connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); |
| 1226 | 1316 | connect(&_entityEditSender, &EntityEditPacketSender::addingEntityWithCertificate, this, &Application::addingEntityWithCertificate); |
| 1227 | 1317 | |
| 1228 | − const char** constArgv = const_cast<const char**>(argv); | |
| 1229 | 1318 | QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); |
| 1230 | 1319 | bool success; |
| 1231 | 1320 | int concurrentDownloads = concurrentDownloadsStr.toInt(&success); |
| @@ -1268,67 +1357,88 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 36 unmodified lines | ||
| 1268 | 1357 | // Make sure the window is set to the correct size by processing the pending events |
| 1269 | 1358 | QCoreApplication::processEvents(); |
| 1270 | 1359 | _glWidget->createContext(); |
| 1271 | − _glWidget->makeCurrent(); | |
| 1272 | 1360 | |
| 1361 | + // Create the main thread context, the GPU backend | |
| 1273 | 1362 | initializeGL(); |
| 1363 | + qCDebug(interfaceapp, "Initialized GL"); | |
| 1364 | + | |
| 1365 | + // Initialize the display plugin architecture | |
| 1366 | + initializeDisplayPlugins(); | |
| 1367 | + qCDebug(interfaceapp, "Initialized Display"); | |
| 1368 | + | |
| 1369 | + // Create the rendering engine. This can be slow on some machines due to lots of | |
| 1370 | + // GPU pipeline creation. | |
| 1371 | + initializeRenderEngine(); | |
| 1372 | + qCDebug(interfaceapp, "Initialized Render Engine."); | |
| 1373 | + | |
| 1374 | + // Initialize the user interface and menu system | |
| 1375 | + // Needs to happen AFTER the render engine initialization to access its configuration | |
| 1376 | + initializeUi(); | |
| 1377 | + | |
| 1378 | + init(); | |
| 1379 | + qCDebug(interfaceapp, "init() complete."); | |
| 1380 | + | |
| 1381 | + // create thread for parsing of octree data independent of the main network and rendering threads | |
| 1382 | + _octreeProcessor.initialize(_enableProcessOctreeThread); | |
| 1383 | + connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); | |
| 1384 | + _entityEditSender.initialize(_enableProcessOctreeThread); | |
| 1385 | + | |
| 1386 | + _idleLoopStdev.reset(); | |
| 1387 | + | |
| 1388 | + // update before the first render | |
| 1389 | + update(0); | |
| 1390 | + | |
| 1274 | 1391 | // Make sure we don't time out during slow operations at startup |
| 1275 | 1392 | updateHeartbeat(); |
| 1276 | 1393 | |
| 1277 | − // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. | |
| 1278 | − // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. | |
| 1279 | − static const QString TESTER = "HIFI_TESTER"; | |
| 1280 | − auto gpuIdent = GPUIdent::getInstance(); | |
| 1281 | − auto glContextData = getGLContextData(); | |
| 1282 | − QJsonObject properties = { | |
| 1283 | − { "version", applicationVersion() }, | |
| 1284 | − { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, | |
| 1285 | − { "previousSessionCrashed", _previousSessionCrashed }, | |
| 1286 | − { "previousSessionRuntime", sessionRunTime.get() }, | |
| 1287 | − { "cpu_architecture", QSysInfo::currentCpuArchitecture() }, | |
| 1288 | − { "kernel_type", QSysInfo::kernelType() }, | |
| 1289 | − { "kernel_version", QSysInfo::kernelVersion() }, | |
| 1290 | − { "os_type", QSysInfo::productType() }, | |
| 1291 | − { "os_version", QSysInfo::productVersion() }, | |
| 1292 | − { "gpu_name", gpuIdent->getName() }, | |
| 1293 | − { "gpu_driver", gpuIdent->getDriver() }, | |
| 1294 | − { "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) }, | |
| 1295 | − { "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) }, | |
| 1296 | − { "gl_version", glContextData["version"] }, | |
| 1297 | − { "gl_vender", glContextData["vendor"] }, | |
| 1298 | − { "gl_sl_version", glContextData["sl_version"] }, | |
| 1299 | − { "gl_renderer", glContextData["renderer"] }, | |
| 1300 | − { "ideal_thread_count", QThread::idealThreadCount() } | |
| 1301 | − }; | |
| 1302 | − auto macVersion = QSysInfo::macVersion(); | |
| 1303 | − if (macVersion != QSysInfo::MV_None) { | |
| 1304 | − properties["os_osx_version"] = QSysInfo::macVersion(); | |
| 1305 | − } | |
| 1306 | − auto windowsVersion = QSysInfo::windowsVersion(); | |
| 1307 | − if (windowsVersion != QSysInfo::WV_None) { | |
| 1308 | − properties["os_win_version"] = QSysInfo::windowsVersion(); | |
| 1394 | + constexpr auto INSTALLER_INI_NAME = "installer.ini"; | |
| 1395 | + auto iniPath = QDir(applicationDirPath()).filePath(INSTALLER_INI_NAME); | |
| 1396 | + QFile installerFile { iniPath }; | |
| 1397 | + std::unordered_map<QString, QString> installerKeyValues; | |
| 1398 | + if (installerFile.open(QIODevice::ReadOnly)) { | |
| 1399 | + while (!installerFile.atEnd()) { | |
| 1400 | + auto line = installerFile.readLine(); | |
| 1401 | + if (!line.isEmpty()) { | |
| 1402 | + auto index = line.indexOf("="); | |
| 1403 | + if (index >= 0) { | |
| 1404 | + installerKeyValues[line.mid(0, index).trimmed()] = line.mid(index + 1).trimmed(); | |
| 1405 | + } | |
| 1406 | + } | |
| 1407 | + } | |
| 1309 | 1408 | } |
| 1310 | 1409 | |
| 1311 | − ProcessorInfo procInfo; | |
| 1312 | − if (getProcessorInfo(procInfo)) { | |
| 1313 | − properties["processor_core_count"] = procInfo.numProcessorCores; | |
| 1314 | − properties["logical_processor_count"] = procInfo.numLogicalProcessors; | |
| 1315 | − properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1; | |
| 1316 | − properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2; | |
| 1317 | − properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3; | |
| 1410 | + // In practice we shouldn't run across installs that don't have a known installer type. | |
| 1411 | + // Client or Client+Server installs should always have the installer.ini next to their | |
| 1412 | + // respective interface.exe, and Steam installs will be detected as such. If a user were | |
| 1413 | + // to delete the installer.ini, though, and as an example, we won't know the context of the | |
| 1414 | + // original install. | |
| 1415 | + constexpr auto INSTALLER_KEY_TYPE = "type"; | |
| 1416 | + constexpr auto INSTALLER_KEY_CAMPAIGN = "campaign"; | |
| 1417 | + constexpr auto INSTALLER_TYPE_UNKNOWN = "unknown"; | |
| 1418 | + constexpr auto INSTALLER_TYPE_STEAM = "steam"; | |
| 1419 | + | |
| 1420 | + auto typeIt = installerKeyValues.find(INSTALLER_KEY_TYPE); | |
| 1421 | + QString installerType = INSTALLER_TYPE_UNKNOWN; | |
| 1422 | + if (typeIt == installerKeyValues.end()) { | |
| 1423 | + if (property(hifi::properties::STEAM).toBool()) { | |
| 1424 | + installerType = INSTALLER_TYPE_STEAM; | |
| 1425 | + } | |
| 1426 | + } else { | |
| 1427 | + installerType = typeIt->second; | |
| 1318 | 1428 | } |
| 1319 | 1429 | |
| 1430 | + auto campaignIt = installerKeyValues.find(INSTALLER_KEY_CAMPAIGN); | |
| 1431 | + QString installerCampaign = campaignIt != installerKeyValues.end() ? campaignIt->second : ""; | |
| 1432 | + | |
| 1433 | + qDebug() << "Detected installer type:" << installerType; | |
| 1434 | + qDebug() << "Detected installer campaign:" << installerCampaign; | |
| 1435 | + | |
| 1320 | 1436 | // add firstRun flag from settings to launch event |
| 1321 | 1437 | Setting::Handle<bool> firstRun { Settings::firstRun, true }; |
| 1322 | 1438 | |
| 1323 | − // once the settings have been loaded, check if we need to flip the default for UserActivityLogger | |
| 1324 | − auto& userActivityLogger = UserActivityLogger::getInstance(); | |
| 1325 | − if (!userActivityLogger.isDisabledSettingSet()) { | |
| 1326 | − // the user activity logger is opt-out for Interface | |
| 1327 | − // but it's defaulted to disabled for other targets | |
| 1328 | − // so we need to enable it here if it has never been disabled by the user | |
| 1329 | − userActivityLogger.disable(false); | |
| 1330 | − } | |
| 1439 | + QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); | |
| 1331 | 1440 | |
| 1441 | + auto& userActivityLogger = UserActivityLogger::getInstance(); | |
| 1332 | 1442 | if (userActivityLogger.isEnabled()) { |
| 1333 | 1443 | // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. |
| 1334 | 1444 | // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. |
| @@ -1338,6 +1448,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 3 unmodified lines | ||
| 1338 | 1448 | QJsonObject properties = { |
| 1339 | 1449 | { "version", applicationVersion() }, |
| 1340 | 1450 | { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, |
| 1451 | + { "installer_campaign", installerCampaign }, | |
| 1452 | + { "installer_type", installerType }, | |
| 1453 | + { "build_type", BuildInfo::BUILD_TYPE_STRING }, | |
| 1341 | 1454 | { "previousSessionCrashed", _previousSessionCrashed }, |
| 1342 | 1455 | { "previousSessionRuntime", sessionRunTime.get() }, |
| 1343 | 1456 | { "cpu_architecture", QSysInfo::currentCpuArchitecture() }, |
| @@ -1376,11 +1489,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 32 unmodified lines | ||
| 1376 | 1489 | properties["first_run"] = firstRun.get(); |
| 1377 | 1490 | |
| 1378 | 1491 | // add the user's machine ID to the launch event |
| 1379 | − properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); | |
| 1492 | + properties["machine_fingerprint"] = machineFingerPrint; | |
| 1380 | 1493 | |
| 1381 | 1494 | userActivityLogger.logAction("launch", properties); |
| 1382 | 1495 | } |
| 1383 | 1496 | |
| 1497 | + setCrashAnnotation("machine_fingerprint", machineFingerPrint.toStdString()); | |
| 1498 | + | |
| 1384 | 1499 | _entityEditSender.setMyAvatar(myAvatar.get()); |
| 1385 | 1500 | |
| 1386 | 1501 | // The entity octree will have to know about MyAvatar for the parentJointName import |
| @@ -1506,7 +1621,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 119 unmodified lines | ||
| 1506 | 1621 | |
| 1507 | 1622 | if (state) { |
| 1508 | 1623 | if (action == controller::toInt(controller::Action::TOGGLE_MUTE)) { |
| 1509 | − DependencyManager::get<AudioClient>()->toggleMute(); | |
| 1624 | + auto audioClient = DependencyManager::get<AudioClient>(); | |
| 1625 | + audioClient->setMuted(!audioClient->isMuted()); | |
| 1510 | 1626 | } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) { |
| 1511 | 1627 | cycleCamera(); |
| 1512 | 1628 | } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { |
| @@ -1591,6 +1707,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 78 unmodified lines | ||
| 1591 | 1707 | |
| 1592 | 1708 | loadSettings(); |
| 1593 | 1709 | |
| 1710 | + updateVerboseLogging(); | |
| 1711 | + | |
| 1594 | 1712 | // Now that we've loaded the menu and thus switched to the previous display plugin |
| 1595 | 1713 | // we can unlock the desktop repositioning code, since all the positions will be |
| 1596 | 1714 | // relative to the desktop size for this plugin |
| @@ -1655,8 +1773,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 58 unmodified lines | ||
| 1655 | 1773 | // If launched from Steam, let it handle updates |
| 1656 | 1774 | const QString HIFI_NO_UPDATER_COMMAND_LINE_KEY = "--no-updater"; |
| 1657 | 1775 | bool noUpdater = arguments().indexOf(HIFI_NO_UPDATER_COMMAND_LINE_KEY) != -1; |
| 1658 | − if (!noUpdater) { | |
| 1659 | − auto applicationUpdater = DependencyManager::get<AutoUpdater>(); | |
| 1776 | + bool buildCanUpdate = BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable | |
| 1777 | + || BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master; | |
| 1778 | + if (!noUpdater && buildCanUpdate) { | |
| 1779 | + constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only"; | |
| 1780 | + | |
| 1781 | + auto applicationUpdater = DependencyManager::set<AutoUpdater>(); | |
| 1782 | + | |
| 1783 | + AutoUpdater::InstallerType type = installerType == INSTALLER_TYPE_CLIENT_ONLY | |
| 1784 | + ? AutoUpdater::InstallerType::CLIENT_ONLY : AutoUpdater::InstallerType::FULL; | |
| 1785 | + | |
| 1786 | + applicationUpdater->setInstallerType(type); | |
| 1787 | + applicationUpdater->setInstallerCampaign(installerCampaign); | |
| 1660 | 1788 | connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); |
| 1661 | 1789 | applicationUpdater->checkForUpdate(); |
| 1662 | 1790 | } |
| @@ -2035,14 +2163,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 372 unmodified lines | ||
| 2035 | 2163 | return entityServerNode && !isPhysicsEnabled(); |
| 2036 | 2164 | }); |
| 2037 | 2165 | |
| 2038 | − _snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snap.wav")); | |
| 2039 | − | |
| 2166 | + _snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snapshot/snap.wav")); | |
| 2167 | + | |
| 2040 | 2168 | QVariant testProperty = property(hifi::properties::TEST); |
| 2041 | 2169 | qDebug() << testProperty; |
| 2042 | 2170 | if (testProperty.isValid()) { |
| 2043 | 2171 | auto scriptEngines = DependencyManager::get<ScriptEngines>(); |
| 2044 | 2172 | const auto testScript = property(hifi::properties::TEST).toUrl(); |
| 2045 | − scriptEngines->loadScript(testScript, false); | |
| 2173 | + | |
| 2174 | + // Set last parameter to exit interface when the test script finishes, if so requested | |
| 2175 | + scriptEngines->loadScript(testScript, false, false, false, false, quitWhenFinished); | |
| 2176 | + | |
| 2177 | + // This is done so we don't get a "connection time-out" message when we haven't passed in a URL. | |
| 2178 | + if (arguments().contains("--url")) { | |
| 2179 | + auto reply = SandboxUtils::getStatus(); | |
| 2180 | + connect(reply, &QNetworkReply::finished, this, [=] { | |
| 2181 | + handleSandboxStatus(reply); | |
| 2182 | + }); | |
| 2183 | + } | |
| 2046 | 2184 | } else { |
| 2047 | 2185 | PROFILE_RANGE(render, "GetSandboxStatus"); |
| 2048 | 2186 | auto reply = SandboxUtils::getStatus(); |
| @@ -2114,6 +2252,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 65 unmodified lines | ||
| 2114 | 2252 | _pendingRenderEvent = false; |
| 2115 | 2253 | |
| 2116 | 2254 | qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); |
| 2255 | + | |
| 2256 | +#if defined(Q_OS_ANDROID) | |
| 2257 | + AndroidHelper::instance().init(); | |
| 2258 | + connect(&AndroidHelper::instance(), &AndroidHelper::enterBackground, this, &Application::enterBackground); | |
| 2259 | + connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); | |
| 2260 | + AndroidHelper::instance().notifyLoadComplete(); | |
| 2261 | +#endif | |
| 2262 | +} | |
| 2263 | + | |
| 2264 | +void Application::updateVerboseLogging() { | |
| 2265 | + auto menu = Menu::getInstance(); | |
| 2266 | + if (!menu) { | |
| 2267 | + return; | |
| 2268 | + } | |
| 2269 | + bool enable = menu->isOptionChecked(MenuOption::VerboseLogging); | |
| 2270 | + | |
| 2271 | + QString rules = | |
| 2272 | + "hifi.*.debug=%1\n" | |
| 2273 | + "hifi.*.info=%1\n" | |
| 2274 | + "hifi.audio-stream.debug=false\n" | |
| 2275 | + "hifi.audio-stream.info=false"; | |
| 2276 | + rules = rules.arg(enable ? "true" : "false"); | |
| 2277 | + QLoggingCategory::setFilterRules(rules); | |
| 2117 | 2278 | } |
| 2118 | 2279 | |
| 2119 | 2280 | void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { |
| @@ -2226,6 +2387,10 @@ void Application::onAboutToQuit() { · 106 unmodified lines | ||
| 2226 | 2387 | } |
| 2227 | 2388 | } |
| 2228 | 2389 | |
| 2390 | + // The active display plugin needs to be loaded before the menu system is active, | |
| 2391 | + // so its persisted explicitly here | |
| 2392 | + Setting::Handle<QString>{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME }.set(getActiveDisplayPlugin()->getName()); | |
| 2393 | + | |
| 2229 | 2394 | getActiveDisplayPlugin()->deactivate(); |
| 2230 | 2395 | if (_autoSwitchDisplayModeSupportedHMDPlugin |
| 2231 | 2396 | && _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) { |
| @@ -2315,6 +2480,7 @@ void Application::cleanupBeforeQuit() { · 83 unmodified lines | ||
| 2315 | 2480 | } |
| 2316 | 2481 | |
| 2317 | 2482 | _window->saveGeometry(); |
| 2483 | + _gpuContext->shutdown(); | |
| 2318 | 2484 | |
| 2319 | 2485 | // Destroy third party processes after scripts have finished using them. |
| 2320 | 2486 | #ifdef HAVE_DDE |
| @@ -2432,48 +2598,171 @@ void Application::initializeGL() { · 111 unmodified lines | ||
| 2432 | 2598 | _isGLInitialized = true; |
| 2433 | 2599 | } |
| 2434 | 2600 | |
| 2435 | − // Build a shared canvas / context for the Chromium processes | |
| 2436 | − _glWidget->makeCurrent(); | |
| 2601 | + if (!_glWidget->makeCurrent()) { | |
| 2602 | + qCWarning(interfaceapp, "Unable to make window context current"); | |
| 2603 | + } | |
| 2437 | 2604 | |
| 2438 | 2605 | #if !defined(DISABLE_QML) |
| 2439 | − // Chromium rendering uses some GL functions that prevent nSight from capturing | |
| 2440 | − // frames, so we only create the shared context if nsight is NOT active. | |
| 2441 | − if (!nsightActive()) { | |
| 2442 | − _chromiumShareContext = new OffscreenGLCanvas(); | |
| 2443 | − _chromiumShareContext->setObjectName("ChromiumShareContext"); | |
| 2444 | − _chromiumShareContext->create(_glWidget->qglContext()); | |
| 2445 | − _chromiumShareContext->makeCurrent(); | |
| 2446 | − if (!_chromiumShareContext->makeCurrent()) { | |
| 2447 | − qCWarning(interfaceapp, "Unable to make chromium shared context current"); | |
| 2448 | − } | |
| 2449 | − qt_gl_set_global_share_context(_chromiumShareContext->getContext()); | |
| 2450 | − } else { | |
| 2451 | − qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering"; | |
| 2606 | + // Build a shared canvas / context for the Chromium processes | |
| 2607 | + { | |
| 2608 | + // Disable signed distance field font rendering on ATI/AMD GPUs, due to | |
| 2609 | + // https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app | |
| 2610 | + std::string vendor{ (const char*)glGetString(GL_VENDOR) }; | |
| 2611 | + if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) { | |
| 2612 | + qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text")); | |
| 2613 | + } | |
| 2614 | + | |
| 2615 | + // Chromium rendering uses some GL functions that prevent nSight from capturing | |
| 2616 | + // frames, so we only create the shared context if nsight is NOT active. | |
| 2617 | + if (!nsightActive()) { | |
| 2618 | + _chromiumShareContext = new OffscreenGLCanvas(); | |
| 2619 | + _chromiumShareContext->setObjectName("ChromiumShareContext"); | |
| 2620 | + _chromiumShareContext->create(_glWidget->qglContext()); | |
| 2621 | + if (!_chromiumShareContext->makeCurrent()) { | |
| 2622 | + qCWarning(interfaceapp, "Unable to make chromium shared context current"); | |
| 2623 | + } | |
| 2624 | + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); | |
| 2625 | + _chromiumShareContext->doneCurrent(); | |
| 2626 | + // Restore the GL widget context | |
| 2627 | + if (!_glWidget->makeCurrent()) { | |
| 2628 | + qCWarning(interfaceapp, "Unable to make window context current"); | |
| 2629 | + } | |
| 2630 | + } else { | |
| 2631 | + qCWarning(interfaceapp) << "nSight detected, disabling chrome rendering"; | |
| 2632 | + } | |
| 2452 | 2633 | } |
| 2453 | 2634 | #endif |
| 2454 | 2635 | |
| 2455 | 2636 | // Build a shared canvas / context for the QML rendering |
| 2637 | + { | |
| 2638 | + _qmlShareContext = new OffscreenGLCanvas(); | |
| 2639 | + _qmlShareContext->setObjectName("QmlShareContext"); | |
| 2640 | + _qmlShareContext->create(_glWidget->qglContext()); | |
| 2641 | + if (!_qmlShareContext->makeCurrent()) { | |
| 2642 | + qCWarning(interfaceapp, "Unable to make QML shared context current"); | |
| 2643 | + } | |
| 2644 | + OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); | |
| 2645 | + _qmlShareContext->doneCurrent(); | |
| 2646 | + if (!_glWidget->makeCurrent()) { | |
| 2647 | + qCWarning(interfaceapp, "Unable to make window context current"); | |
| 2648 | + } | |
| 2649 | + } | |
| 2650 | + | |
| 2651 | + // Build an offscreen GL context for the main thread. | |
| 2652 | + _offscreenContext = new OffscreenGLCanvas(); | |
| 2653 | + _offscreenContext->setObjectName("MainThreadContext"); | |
| 2654 | + _offscreenContext->create(_glWidget->qglContext()); | |
| 2655 | + if (!_offscreenContext->makeCurrent()) { | |
| 2656 | + qFatal("Unable to make offscreen context current"); | |
| 2657 | + } | |
| 2658 | + _offscreenContext->doneCurrent(); | |
| 2659 | + _offscreenContext->setThreadContext(); | |
| 2660 | + | |
| 2456 | 2661 | _glWidget->makeCurrent(); |
| 2457 | − _qmlShareContext = new OffscreenGLCanvas(); | |
| 2458 | − _qmlShareContext->setObjectName("QmlShareContext"); | |
| 2459 | − _qmlShareContext->create(_glWidget->qglContext()); | |
| 2460 | − if (!_qmlShareContext->makeCurrent()) { | |
| 2461 | − qCWarning(interfaceapp, "Unable to make QML shared context current"); | |
| 2662 | + glClearColor(0.2f, 0.2f, 0.2f, 1); | |
| 2663 | + glClear(GL_COLOR_BUFFER_BIT); | |
| 2664 | + _glWidget->swapBuffers(); | |
| 2665 | + | |
| 2666 | + // Move the GL widget context to the render event handler thread | |
| 2667 | + _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); | |
| 2668 | + if (!_offscreenContext->makeCurrent()) { | |
| 2669 | + qFatal("Unable to make offscreen context current"); | |
| 2462 | 2670 | } |
| 2463 | − OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); | |
| 2464 | − _qmlShareContext->doneCurrent(); | |
| 2465 | 2671 | |
| 2672 | + // Create the GPU backend | |
| 2673 | + | |
| 2674 | + // Requires the window context, because that's what's used in the actual rendering | |
| 2675 | + // and the GPU backend will make things like the VAO which cannot be shared across | |
| 2676 | + // contexts | |
| 2466 | 2677 | _glWidget->makeCurrent(); |
| 2467 | 2678 | gpu::Context::init<gpu::gl::GLBackend>(); |
| 2468 | 2679 | qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK, |
| 2469 | 2680 | QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram))); |
| 2470 | − _gpuContext = std::make_shared<gpu::Context>(); | |
| 2471 | − // The gpu context can make child contexts for transfers, so | |
| 2472 | − // we need to restore primary rendering context | |
| 2473 | 2681 | _glWidget->makeCurrent(); |
| 2682 | + _gpuContext = std::make_shared<gpu::Context>(); | |
| 2683 | + | |
| 2684 | + DependencyManager::get<TextureCache>()->setGPUContext(_gpuContext); | |
| 2685 | + | |
| 2686 | + // Restore the default main thread context | |
| 2687 | + _offscreenContext->makeCurrent(); | |
| 2688 | +} | |
| 2689 | + | |
| 2690 | +static const QString SPLASH_SKYBOX{ "{\"ProceduralEntity\":{ \"version\":2, \"shaderUrl\":\"qrc:///shaders/splashSkybox.frag\" } }" }; | |
| 2691 | + | |
| 2692 | +void Application::initializeDisplayPlugins() { | |
| 2693 | + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); | |
| 2694 | + Setting::Handle<QString> activeDisplayPluginSetting{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME, displayPlugins.at(0)->getName() }; | |
| 2695 | + auto lastActiveDisplayPluginName = activeDisplayPluginSetting.get(); | |
| 2696 | + | |
| 2697 | + auto defaultDisplayPlugin = displayPlugins.at(0); | |
| 2698 | + // Once time initialization code | |
| 2699 | + DisplayPluginPointer targetDisplayPlugin; | |
| 2700 | + foreach(auto displayPlugin, displayPlugins) { | |
| 2701 | + displayPlugin->setContext(_gpuContext); | |
| 2702 | + if (displayPlugin->getName() == lastActiveDisplayPluginName) { | |
| 2703 | + targetDisplayPlugin = displayPlugin; | |
| 2704 | + } | |
| 2705 | + QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, | |
| 2706 | + [this](const QSize& size) { resizeGL(); }); | |
| 2707 | + QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); | |
| 2708 | + } | |
| 2709 | + | |
| 2710 | + // The default display plugin needs to be activated first, otherwise the display plugin thread | |
| 2711 | + // may be launched by an external plugin, which is bad | |
| 2712 | + setDisplayPlugin(defaultDisplayPlugin); | |
| 2713 | + | |
| 2714 | + // Now set the desired plugin if it's not the same as the default plugin | |
| 2715 | + if (targetDisplayPlugin != defaultDisplayPlugin) { | |
| 2716 | + setDisplayPlugin(targetDisplayPlugin); | |
| 2717 | + } | |
| 2718 | + | |
| 2719 | + // Submit a default frame to render until the engine starts up | |
| 2720 | + updateRenderArgs(0.0f); | |
| 2721 | + | |
| 2722 | + _offscreenContext->makeCurrent(); | |
| 2723 | + | |
| 2724 | +#define ENABLE_SPLASH_FRAME 0 | |
| 2725 | +#if ENABLE_SPLASH_FRAME | |
| 2726 | + { | |
| 2727 | + QMutexLocker viewLocker(&_renderArgsMutex); | |
| 2728 | + | |
| 2729 | + if (_appRenderArgs._isStereo) { | |
| 2730 | + _gpuContext->enableStereo(true); | |
| 2731 | + _gpuContext->setStereoProjections(_appRenderArgs._eyeProjections); | |
| 2732 | + _gpuContext->setStereoViews(_appRenderArgs._eyeOffsets); | |
| 2733 | + } | |
| 2734 | + | |
| 2735 | + // Frame resources | |
| 2736 | + auto framebufferCache = DependencyManager::get<FramebufferCache>(); | |
| 2737 | + gpu::FramebufferPointer finalFramebuffer = framebufferCache->getFramebuffer(); | |
| 2738 | + std::shared_ptr<ProceduralSkybox> procedural = std::make_shared<ProceduralSkybox>(); | |
| 2739 | + procedural->parse(SPLASH_SKYBOX); | |
| 2740 | + | |
| 2741 | + _gpuContext->beginFrame(_appRenderArgs._view, _appRenderArgs._headPose); | |
| 2742 | + gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) { | |
| 2743 | + batch.resetStages(); | |
| 2744 | + batch.enableStereo(false); | |
| 2745 | + batch.setFramebuffer(finalFramebuffer); | |
| 2746 | + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, { 0, 0, 0, 1 }); | |
| 2747 | + batch.enableSkybox(true); | |
| 2748 | + batch.enableStereo(_appRenderArgs._isStereo); | |
| 2749 | + batch.setViewportTransform({ 0, 0, finalFramebuffer->getSize() }); | |
| 2750 | + procedural->render(batch, _appRenderArgs._renderArgs.getViewFrustum()); | |
| 2751 | + }); | |
| 2752 | + auto frame = _gpuContext->endFrame(); | |
| 2753 | + frame->frameIndex = 0; | |
| 2754 | + frame->framebuffer = finalFramebuffer; | |
| 2755 | + frame->pose = _appRenderArgs._headPose; | |
| 2756 | + frame->framebufferRecycler = [framebufferCache, procedural](const gpu::FramebufferPointer& framebuffer) { | |
| 2757 | + framebufferCache->releaseFramebuffer(framebuffer); | |
| 2758 | + }; | |
| 2759 | + _displayPlugin->submitFrame(frame); | |
| 2760 | + } | |
| 2761 | +#endif | |
| 2762 | +} | |
| 2474 | 2763 | |
| 2475 | − initDisplay(); | |
| 2476 | − qCDebug(interfaceapp, "Initialized Display."); | |
| 2764 | +void Application::initializeRenderEngine() { | |
| 2765 | + _offscreenContext->makeCurrent(); | |
| 2477 | 2766 | |
| 2478 | 2767 | // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. |
| 2479 | 2768 | DeadlockWatchdogThread::withPause([&] { |
| @@ -2490,67 +2779,12 @@ void Application::initializeGL() { · 10 unmodified lines | ||
| 2490 | 2779 | // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. |
| 2491 | 2780 | DependencyManager::get<GeometryCache>()->initializeShapePipelines(); |
| 2492 | 2781 | }); |
| 2493 | − | |
| 2494 | − _offscreenContext = new OffscreenGLCanvas(); | |
| 2495 | − _offscreenContext->setObjectName("MainThreadContext"); | |
| 2496 | − _offscreenContext->create(_glWidget->qglContext()); | |
| 2497 | − if (!_offscreenContext->makeCurrent()) { | |
| 2498 | − qFatal("Unable to make offscreen context current"); | |
| 2499 | − } | |
| 2500 | − _offscreenContext->doneCurrent(); | |
| 2501 | − _offscreenContext->setThreadContext(); | |
| 2502 | − _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); | |
| 2503 | − | |
| 2504 | − // The UI can't be created until the primary OpenGL | |
| 2505 | − // context is created, because it needs to share | |
| 2506 | − // texture resources | |
| 2507 | − // Needs to happen AFTER the render engine initialization to access its configuration | |
| 2508 | − if (!_offscreenContext->makeCurrent()) { | |
| 2509 | − qFatal("Unable to make offscreen context current"); | |
| 2510 | − } | |
| 2511 | − | |
| 2512 | − initializeUi(); | |
| 2513 | − qCDebug(interfaceapp, "Initialized Offscreen UI."); | |
| 2514 | − | |
| 2515 | − if (!_offscreenContext->makeCurrent()) { | |
| 2516 | − qFatal("Unable to make offscreen context current"); | |
| 2517 | − } | |
| 2518 | − init(); | |
| 2519 | − qCDebug(interfaceapp, "init() complete."); | |
| 2520 | − | |
| 2521 | − // create thread for parsing of octree data independent of the main network and rendering threads | |
| 2522 | − _octreeProcessor.initialize(_enableProcessOctreeThread); | |
| 2523 | − connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); | |
| 2524 | − _entityEditSender.initialize(_enableProcessOctreeThread); | |
| 2525 | − | |
| 2526 | − _idleLoopStdev.reset(); | |
| 2527 | − | |
| 2528 | − | |
| 2529 | − // Restore the primary GL content for the main thread | |
| 2530 | − if (!_offscreenContext->makeCurrent()) { | |
| 2531 | − qFatal("Unable to make offscreen context current"); | |
| 2532 | − } | |
| 2533 | − | |
| 2534 | − // update before the first render | |
| 2535 | − update(0); | |
| 2536 | 2782 | } |
| 2537 | 2783 | |
| 2538 | 2784 | extern void setupPreferences(); |
| 2785 | +static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false); | |
| 2539 | 2786 | |
| 2540 | 2787 | void Application::initializeUi() { |
| 2541 | − // Make sure all QML surfaces share the main thread GL context | |
| 2542 | − OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext()); | |
| 2543 | − OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "OverlayWindowTest.qml" }, | |
| 2544 | − [](QQmlContext* context) { | |
| 2545 | − qDebug() << "Whitelist OverlayWindow worked"; | |
| 2546 | − context->setContextProperty("OverlayWindowTestString", "TestWorked"); | |
| 2547 | − }); | |
| 2548 | − OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "hifi/audio/Audio.qml" }, | |
| 2549 | − [](QQmlContext* context) { | |
| 2550 | − qDebug() << "QQQ" << __FUNCTION__ << "Whitelist Audio worked"; | |
| 2551 | − }); | |
| 2552 | − | |
| 2553 | − | |
| 2554 | 2788 | AddressBarDialog::registerType(); |
| 2555 | 2789 | ErrorDialog::registerType(); |
| 2556 | 2790 | LoginDialog::registerType(); |
| @@ -2565,6 +2799,7 @@ void Application::initializeUi() { · 8 unmodified lines | ||
| 2565 | 2799 | QUrl{ "hifi/commerce/common/EmulatedMarketplaceHeader.qml" }, |
| 2566 | 2800 | QUrl{ "hifi/commerce/common/FirstUseTutorial.qml" }, |
| 2567 | 2801 | QUrl{ "hifi/commerce/common/SortableListModel.qml" }, |
| 2802 | + QUrl{ "hifi/commerce/common/sendAsset/SendAsset.qml" }, | |
| 2568 | 2803 | QUrl{ "hifi/commerce/inspectionCertificate/InspectionCertificate.qml" }, |
| 2569 | 2804 | QUrl{ "hifi/commerce/purchases/PurchasedItem.qml" }, |
| 2570 | 2805 | QUrl{ "hifi/commerce/purchases/Purchases.qml" }, |
| @@ -2577,7 +2812,6 @@ void Application::initializeUi() { · 6 unmodified lines | ||
| 2577 | 2812 | QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" }, |
| 2578 | 2813 | QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" }, |
| 2579 | 2814 | QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" }, |
| 2580 | − QUrl{ "hifi/commerce/wallet/sendMoney/SendMoney.qml" }, | |
| 2581 | 2815 | QUrl{ "hifi/commerce/wallet/Wallet.qml" }, |
| 2582 | 2816 | QUrl{ "hifi/commerce/wallet/WalletHome.qml" }, |
| 2583 | 2817 | QUrl{ "hifi/commerce/wallet/WalletSetup.qml" }, |
| @@ -2623,6 +2857,9 @@ void Application::initializeUi() { · 39 unmodified lines | ||
| 2623 | 2857 | offscreenUi->resume(); |
| 2624 | 2858 | connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){ |
| 2625 | 2859 | resizeGL(); |
| 2860 | + if (_touchscreenVirtualPadDevice) { | |
| 2861 | + _touchscreenVirtualPadDevice->resize(); | |
| 2862 | + } | |
| 2626 | 2863 | }); |
| 2627 | 2864 | |
| 2628 | 2865 | // This will set up the input plugins UI |
| @@ -2636,6 +2873,13 @@ void Application::initializeUi() { · 7 unmodified lines | ||
| 2636 | 2873 | } |
| 2637 | 2874 | if (TouchscreenVirtualPadDevice::NAME == inputPlugin->getName()) { |
| 2638 | 2875 | _touchscreenVirtualPadDevice = std::dynamic_pointer_cast<TouchscreenVirtualPadDevice>(inputPlugin); |
| 2876 | +#if defined(Q_OS_ANDROID) | |
| 2877 | + auto& virtualPadManager = VirtualPad::Manager::instance(); | |
| 2878 | + connect(&virtualPadManager, &VirtualPad::Manager::hapticFeedbackRequested, | |
| 2879 | + this, [](int duration) { | |
| 2880 | + AndroidHelper::instance().performHapticFeedback(duration); | |
| 2881 | + }); | |
| 2882 | +#endif | |
| 2639 | 2883 | } |
| 2640 | 2884 | } |
| 2641 | 2885 | |
| @@ -2652,6 +2896,33 @@ void Application::initializeUi() { · 10 unmodified lines | ||
| 2652 | 2896 | auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>(); |
| 2653 | 2897 | offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1); |
| 2654 | 2898 | offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); |
| 2899 | + | |
| 2900 | + flushMenuUpdates(); | |
| 2901 | + | |
| 2902 | + // Now that the menu is instantiated, ensure the display plugin menu is properly updated | |
| 2903 | + { | |
| 2904 | + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); | |
| 2905 | + // first sort the plugins into groupings: standard, advanced, developer | |
| 2906 | + std::stable_sort(displayPlugins.begin(), displayPlugins.end(), | |
| 2907 | + [](const DisplayPluginPointer& a, const DisplayPluginPointer& b)->bool { return a->getGrouping() < b->getGrouping(); }); | |
| 2908 | + | |
| 2909 | + int dpIndex = 1; | |
| 2910 | + // concatenate the groupings into a single list in the order: standard, advanced, developer | |
| 2911 | + for(const auto& displayPlugin : displayPlugins) { | |
| 2912 | + addDisplayPluginToMenu(displayPlugin, dpIndex, _displayPlugin == displayPlugin); | |
| 2913 | + dpIndex++; | |
| 2914 | + } | |
| 2915 | + | |
| 2916 | + // after all plugins have been added to the menu, add a separator to the menu | |
| 2917 | + auto parent = getPrimaryMenu()->getMenu(MenuOption::OutputMenu); | |
| 2918 | + parent->addSeparator(); | |
| 2919 | + } | |
| 2920 | + | |
| 2921 | + // The display plugins are created before the menu now, so we need to do this here to hide the menu bar | |
| 2922 | + // now that it exists | |
| 2923 | + if (_window && _window->isFullScreen()) { | |
| 2924 | + setFullscreen(nullptr, true); | |
| 2925 | + } | |
| 2655 | 2926 | } |
| 2656 | 2927 | |
| 2657 | 2928 | |
| @@ -2731,6 +3002,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { · 73 unmodified lines | ||
| 2731 | 3002 | surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data()); |
| 2732 | 3003 | surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data()); |
| 2733 | 3004 | surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data()); |
| 3005 | + surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); | |
| 2734 | 3006 | |
| 2735 | 3007 | if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { |
| 2736 | 3008 | surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); |
| @@ -2744,9 +3016,11 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { · 7 unmodified lines | ||
| 2744 | 3016 | auto surfaceContext = DependencyManager::get<OffscreenUi>()->getSurfaceContext(); |
| 2745 | 3017 | surfaceContext->setContextProperty("Stats", Stats::getInstance()); |
| 2746 | 3018 | |
| 3019 | +#if !defined(Q_OS_ANDROID) | |
| 2747 | 3020 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 2748 | 3021 | auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml"); |
| 2749 | 3022 | offscreenUi->show(qml, "AvatarInputsBar"); |
| 3023 | +#endif | |
| 2750 | 3024 | } |
| 2751 | 3025 | |
| 2752 | 3026 | void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { |
| @@ -2797,7 +3071,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { · 44 unmodified lines | ||
| 2797 | 3071 | _thirdPersonHMDCameraBoomValid = false; |
| 2798 | 3072 | |
| 2799 | 3073 | _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); |
| 2800 | − if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { | |
| 3074 | + if (isOptionChecked(MenuOption::CenterPlayerInView)) { | |
| 2801 | 3075 | _myCamera.setPosition(myAvatar->getDefaultEyePosition() |
| 2802 | 3076 | + _myCamera.getOrientation() * boomOffset); |
| 2803 | 3077 | } |
| @@ -2974,7 +3248,8 @@ void Application::resizeGL() { · 170 unmodified lines | ||
| 2974 | 3248 | // Set the desired FBO texture size. If it hasn't changed, this does nothing. |
| 2975 | 3249 | // Otherwise, it must rebuild the FBOs |
| 2976 | 3250 | uvec2 framebufferSize = displayPlugin->getRecommendedRenderSize(); |
| 2977 | − uvec2 renderSize = uvec2(vec2(framebufferSize) * getRenderResolutionScale()); | |
| 3251 | + float renderResolutionScale = getRenderResolutionScale(); | |
| 3252 | + uvec2 renderSize = uvec2(vec2(framebufferSize) * renderResolutionScale); | |
| 2978 | 3253 | if (_renderResolution != renderSize) { |
| 2979 | 3254 | _renderResolution = renderSize; |
| 2980 | 3255 | DependencyManager::get<FramebufferCache>()->setFrameBufferSize(fromGlm(renderSize)); |
| @@ -2991,13 +3266,13 @@ void Application::resizeGL() { · 10 unmodified lines | ||
| 2991 | 3266 | } |
| 2992 | 3267 | |
| 2993 | 3268 | DependencyManager::get<OffscreenUi>()->resize(fromGlm(displayPlugin->getRecommendedUiSize())); |
| 3269 | + displayPlugin->setRenderResolutionScale(renderResolutionScale); | |
| 2994 | 3270 | } |
| 2995 | 3271 | |
| 2996 | 3272 | void Application::handleSandboxStatus(QNetworkReply* reply) { |
| 2997 | 3273 | PROFILE_RANGE(render, __FUNCTION__); |
| 2998 | 3274 | |
| 2999 | 3275 | bool sandboxIsRunning = SandboxUtils::readStatus(reply->readAll()); |
| 3000 | − qDebug() << "HandleSandboxStatus" << sandboxIsRunning; | |
| 3001 | 3276 | |
| 3002 | 3277 | enum HandControllerType { |
| 3003 | 3278 | Vive, |
| @@ -3050,16 +3325,17 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 46 unmodified lines | ||
| 3050 | 3325 | // If this is a first run we short-circuit the address passed in |
| 3051 | 3326 | if (firstRun.get()) { |
| 3052 | 3327 | #if !defined(Q_OS_ANDROID) |
| 3053 | − showHelp(); | |
| 3054 | −#endif | |
| 3055 | 3328 | DependencyManager::get<AddressManager>()->goToEntry(); |
| 3056 | 3329 | sentTo = SENT_TO_ENTRY; |
| 3330 | +#endif | |
| 3057 | 3331 | firstRun.set(false); |
| 3058 | 3332 | |
| 3059 | 3333 | } else { |
| 3334 | +#if !defined(Q_OS_ANDROID) | |
| 3060 | 3335 | qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); |
| 3061 | 3336 | DependencyManager::get<AddressManager>()->loadSettings(addressLookupString); |
| 3062 | 3337 | sentTo = SENT_TO_PREVIOUS_LOCATION; |
| 3338 | +#endif | |
| 3063 | 3339 | } |
| 3064 | 3340 | |
| 3065 | 3341 | UserActivityLogger::getInstance().logAction("startup_sent_to", { |
| @@ -3148,6 +3424,10 @@ void Application::loadServerlessDomain(QUrl domainURL) { · 82 unmodified lines | ||
| 3148 | 3424 | tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0); |
| 3149 | 3425 | } |
| 3150 | 3426 | |
| 3427 | + std::map<QString, QString> namedPaths = tmpTree->getNamedPaths(); | |
| 3428 | + nodeList->getDomainHandler().connectedToServerless(namedPaths); | |
| 3429 | + | |
| 3430 | + | |
| 3151 | 3431 | _fullSceneReceivedCounter++; |
| 3152 | 3432 | } |
| 3153 | 3433 | |
| @@ -3369,7 +3649,6 @@ void Application::keyPressEvent(QKeyEvent* event) { · 215 unmodified lines | ||
| 3369 | 3649 | _keysPressed.insert(event->key()); |
| 3370 | 3650 | |
| 3371 | 3651 | _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts |
| 3372 | − | |
| 3373 | 3652 | // if one of our scripts have asked to capture this event, then stop processing it |
| 3374 | 3653 | if (_controllerScriptingInterface->isKeyCaptured(event)) { |
| 3375 | 3654 | return; |
| @@ -3395,9 +3674,21 @@ void Application::keyPressEvent(QKeyEvent* event) { · 19 unmodified lines | ||
| 3395 | 3674 | } |
| 3396 | 3675 | break; |
| 3397 | 3676 | |
| 3398 | − case Qt::Key_1: | |
| 3399 | − case Qt::Key_2: | |
| 3400 | − case Qt::Key_3: | |
| 3677 | + case Qt::Key_1: { | |
| 3678 | + Menu* menu = Menu::getInstance(); | |
| 3679 | + menu->triggerOption(MenuOption::FirstPerson); | |
| 3680 | + break; | |
| 3681 | + } | |
| 3682 | + case Qt::Key_2: { | |
| 3683 | + Menu* menu = Menu::getInstance(); | |
| 3684 | + menu->triggerOption(MenuOption::FullscreenMirror); | |
| 3685 | + break; | |
| 3686 | + } | |
| 3687 | + case Qt::Key_3: { | |
| 3688 | + Menu* menu = Menu::getInstance(); | |
| 3689 | + menu->triggerOption(MenuOption::ThirdPerson); | |
| 3690 | + break; | |
| 3691 | + } | |
| 3401 | 3692 | case Qt::Key_4: |
| 3402 | 3693 | case Qt::Key_5: |
| 3403 | 3694 | case Qt::Key_6: |
| @@ -3447,19 +3738,28 @@ void Application::keyPressEvent(QKeyEvent* event) { · 43 unmodified lines | ||
| 3447 | 3738 | if (isShifted && isMeta) { |
| 3448 | 3739 | Menu::getInstance()->triggerOption(MenuOption::Log); |
| 3449 | 3740 | } else if (isMeta) { |
| 3450 | − Menu::getInstance()->triggerOption(MenuOption::AddressBar); | |
| 3741 | + auto dialogsManager = DependencyManager::get<DialogsManager>(); | |
| 3742 | + dialogsManager->toggleAddressBar(); | |
| 3451 | 3743 | } else if (isShifted) { |
| 3452 | 3744 | Menu::getInstance()->triggerOption(MenuOption::LodTools); |
| 3453 | 3745 | } |
| 3454 | 3746 | break; |
| 3455 | 3747 | |
| 3748 | + case Qt::Key_R: | |
| 3749 | + if (isMeta && !event->isAutoRepeat()) { | |
| 3750 | + DependencyManager::get<ScriptEngines>()->reloadAllScripts(); | |
| 3751 | + DependencyManager::get<OffscreenUi>()->clearCache(); | |
| 3752 | + } | |
| 3753 | + break; | |
| 3754 | + | |
| 3456 | 3755 | case Qt::Key_Asterisk: |
| 3457 | 3756 | Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); |
| 3458 | 3757 | break; |
| 3459 | 3758 | |
| 3460 | 3759 | case Qt::Key_M: |
| 3461 | 3760 | if (isMeta) { |
| 3462 | − DependencyManager::get<AudioClient>()->toggleMute(); | |
| 3761 | + auto audioClient = DependencyManager::get<AudioClient>(); | |
| 3762 | + audioClient->setMuted(!audioClient->isMuted()); | |
| 3463 | 3763 | } |
| 3464 | 3764 | break; |
| 3465 | 3765 | |
| @@ -3479,13 +3779,16 @@ void Application::keyPressEvent(QKeyEvent* event) { · 13 unmodified lines | ||
| 3479 | 3779 | AudioInjectorOptions options; |
| 3480 | 3780 | options.localOnly = true; |
| 3481 | 3781 | options.stereo = true; |
| 3482 | − | |
| 3483 | − if (_snapshotSoundInjector) { | |
| 3484 | − _snapshotSoundInjector->setOptions(options); | |
| 3485 | − _snapshotSoundInjector->restart(); | |
| 3486 | − } else { | |
| 3487 | − QByteArray samples = _snapshotSound->getByteArray(); | |
| 3488 | − _snapshotSoundInjector = AudioInjector::playSound(samples, options); | |
| 3782 | + Setting::Handle<bool> notificationSounds{ MenuOption::NotificationSounds, true}; | |
| 3783 | + Setting::Handle<bool> notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true}; | |
| 3784 | + if (notificationSounds.get() && notificationSoundSnapshot.get()) { | |
| 3785 | + if (_snapshotSoundInjector) { | |
| 3786 | + _snapshotSoundInjector->setOptions(options); | |
| 3787 | + _snapshotSoundInjector->restart(); | |
| 3788 | + } else { | |
| 3789 | + QByteArray samples = _snapshotSound->getByteArray(); | |
| 3790 | + _snapshotSoundInjector = AudioInjector::playSound(samples, options); | |
| 3791 | + } | |
| 3489 | 3792 | } |
| 3490 | 3793 | takeSnapshot(true); |
| 3491 | 3794 | break; |
| @@ -3504,7 +3807,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 12 unmodified lines | ||
| 3504 | 3807 | } else { |
| 3505 | 3808 | showCursor(Cursor::Icon::DEFAULT); |
| 3506 | 3809 | } |
| 3507 | − } else { | |
| 3810 | + } else if (!event->isAutoRepeat()){ | |
| 3508 | 3811 | resetSensors(true); |
| 3509 | 3812 | } |
| 3510 | 3813 | break; |
| @@ -3514,68 +3817,6 @@ void Application::keyPressEvent(QKeyEvent* event) { · 3 unmodified lines | ||
| 3514 | 3817 | Menu::getInstance()->triggerOption(MenuOption::Chat); |
| 3515 | 3818 | break; |
| 3516 | 3819 | |
| 3517 | −#if 0 | |
| 3518 | − case Qt::Key_I: | |
| 3519 | − if (isShifted) { | |
| 3520 | − _myCamera.setEyeOffsetOrientation(glm::normalize( | |
| 3521 | − glm::quat(glm::vec3(0.002f, 0, 0)) * _myCamera.getEyeOffsetOrientation())); | |
| 3522 | − } else { | |
| 3523 | − _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0, 0.001, 0)); | |
| 3524 | − } | |
| 3525 | − updateProjectionMatrix(); | |
| 3526 | − break; | |
| 3527 | − | |
| 3528 | − case Qt::Key_K: | |
| 3529 | − if (isShifted) { | |
| 3530 | − _myCamera.setEyeOffsetOrientation(glm::normalize( | |
| 3531 | − glm::quat(glm::vec3(-0.002f, 0, 0)) * _myCamera.getEyeOffsetOrientation())); | |
| 3532 | − } else { | |
| 3533 | − _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0, -0.001, 0)); | |
| 3534 | − } | |
| 3535 | − updateProjectionMatrix(); | |
| 3536 | − break; | |
| 3537 | − | |
| 3538 | − case Qt::Key_J: | |
| 3539 | − if (isShifted) { | |
| 3540 | − QMutexLocker viewLocker(&_viewMutex); | |
| 3541 | − _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() - 0.1f); | |
| 3542 | − } else { | |
| 3543 | − _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(-0.001, 0, 0)); | |
| 3544 | − } | |
| 3545 | − updateProjectionMatrix(); | |
| 3546 | − break; | |
| 3547 | − | |
| 3548 | − case Qt::Key_M: | |
| 3549 | − if (isShifted) { | |
| 3550 | − QMutexLocker viewLocker(&_viewMutex); | |
| 3551 | − _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() + 0.1f); | |
| 3552 | − } else { | |
| 3553 | − _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0.001, 0, 0)); | |
| 3554 | − } | |
| 3555 | − updateProjectionMatrix(); | |
| 3556 | − break; | |
| 3557 | − | |
| 3558 | − case Qt::Key_U: | |
| 3559 | − if (isShifted) { | |
| 3560 | − _myCamera.setEyeOffsetOrientation(glm::normalize( | |
| 3561 | − glm::quat(glm::vec3(0, 0, -0.002f)) * _myCamera.getEyeOffsetOrientation())); | |
| 3562 | − } else { | |
| 3563 | − _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0, 0, -0.001)); | |
| 3564 | − } | |
| 3565 | − updateProjectionMatrix(); | |
| 3566 | − break; | |
| 3567 | − | |
| 3568 | − case Qt::Key_Y: | |
| 3569 | − if (isShifted) { | |
| 3570 | − _myCamera.setEyeOffsetOrientation(glm::normalize( | |
| 3571 | − glm::quat(glm::vec3(0, 0, 0.002f)) * _myCamera.getEyeOffsetOrientation())); | |
| 3572 | − } else { | |
| 3573 | − _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0, 0, 0.001)); | |
| 3574 | − } | |
| 3575 | − updateProjectionMatrix(); | |
| 3576 | − break; | |
| 3577 | −#endif | |
| 3578 | − | |
| 3579 | 3820 | case Qt::Key_Slash: |
| 3580 | 3821 | Menu::getInstance()->triggerOption(MenuOption::Stats); |
| 3581 | 3822 | break; |
| @@ -3618,6 +3859,12 @@ void Application::keyPressEvent(QKeyEvent* event) { · 36 unmodified lines | ||
| 3618 | 3859 | void Application::keyReleaseEvent(QKeyEvent* event) { |
| 3619 | 3860 | _keysPressed.remove(event->key()); |
| 3620 | 3861 | |
| 3862 | +#if defined(Q_OS_ANDROID) | |
| 3863 | + if (event->key() == Qt::Key_Back) { | |
| 3864 | + event->accept(); | |
| 3865 | + AndroidHelper::instance().requestActivity("Home", false); | |
| 3866 | + } | |
| 3867 | +#endif | |
| 3621 | 3868 | _controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts |
| 3622 | 3869 | |
| 3623 | 3870 | // if one of our scripts have asked to capture this event, then stop processing it |
| @@ -3937,7 +4184,7 @@ bool Application::acceptSnapshot(const QString& urlString) { · 313 unmodified lines | ||
| 3937 | 4184 | QUrl url(urlString); |
| 3938 | 4185 | QString snapshotPath = url.toLocalFile(); |
| 3939 | 4186 | |
| 3940 | − SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath); | |
| 4187 | + SnapshotMetaData* snapshotData = DependencyManager::get<Snapshot>()->parseSnapshotData(snapshotPath); | |
| 3941 | 4188 | if (snapshotData) { |
| 3942 | 4189 | if (!snapshotData->getURL().toString().isEmpty()) { |
| 3943 | 4190 | DependencyManager::get<AddressManager>()->handleLookupString(snapshotData->getURL().toString()); |
| @@ -3952,7 +4199,7 @@ bool Application::acceptSnapshot(const QString& urlString) { · 8 unmodified lines | ||
| 3952 | 4199 | static uint32_t _renderedFrameIndex { INVALID_FRAME }; |
| 3953 | 4200 | |
| 3954 | 4201 | bool Application::shouldPaint() const { |
| 3955 | − if (_aboutToQuit) { | |
| 4202 | + if (_aboutToQuit || _window->isMinimized()) { | |
| 3956 | 4203 | return false; |
| 3957 | 4204 | } |
| 3958 | 4205 | |
| @@ -4348,12 +4595,6 @@ void Application::idle() { · 389 unmodified lines | ||
| 4348 | 4595 | |
| 4349 | 4596 | _overlayConductor.update(secondsSinceLastUpdate); |
| 4350 | 4597 | |
| 4351 | − auto myAvatar = getMyAvatar(); | |
| 4352 | − if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { | |
| 4353 | − Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); | |
| 4354 | − Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); | |
| 4355 | − cameraMenuChanged(); | |
| 4356 | − } | |
| 4357 | 4598 | _gameLoopCounter.increment(); |
| 4358 | 4599 | } |
| 4359 | 4600 | |
| @@ -4518,12 +4759,15 @@ void Application::loadSettings() { · 158 unmodified lines | ||
| 4518 | 4759 | // DONT CHECK IN |
| 4519 | 4760 | //DependencyManager::get<LODManager>()->setAutomaticLODAdjust(false); |
| 4520 | 4761 | |
| 4521 | − Menu::getInstance()->loadSettings(); | |
| 4762 | + auto menu = Menu::getInstance(); | |
| 4763 | + menu->loadSettings(); | |
| 4764 | + | |
| 4765 | + // override the menu option show overlays to always be true on startup | |
| 4766 | + menu->setIsOptionChecked(MenuOption::Overlays, true); | |
| 4522 | 4767 | |
| 4523 | 4768 | // If there is a preferred plugin, we probably messed it up with the menu settings, so fix it. |
| 4524 | 4769 | auto pluginManager = PluginManager::getInstance(); |
| 4525 | 4770 | auto plugins = pluginManager->getPreferredDisplayPlugins(); |
| 4526 | − auto menu = Menu::getInstance(); | |
| 4527 | 4771 | if (plugins.size() > 0) { |
| 4528 | 4772 | for (auto plugin : plugins) { |
| 4529 | 4773 | if (auto action = menu->getActionForOption(plugin->getName())) { |
| @@ -4609,11 +4853,8 @@ QVector<EntityItemID> Application::pasteEntities(float x, float y, float z) { · 79 unmodified lines | ||
| 4609 | 4853 | return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z); |
| 4610 | 4854 | } |
| 4611 | 4855 | |
| 4612 | −void Application::initDisplay() { | |
| 4613 | −} | |
| 4614 | − | |
| 4615 | 4856 | void Application::init() { |
| 4616 | − | |
| 4857 | + _offscreenContext->makeCurrent(); | |
| 4617 | 4858 | // Make sure Login state is up to date |
| 4618 | 4859 | DependencyManager::get<DialogsManager>()->toggleLoginDialog(); |
| 4619 | 4860 | if (!DISABLE_DEFERRED) { |
| @@ -4688,6 +4929,31 @@ void Application::init() { · 68 unmodified lines | ||
| 4688 | 4929 | }, Qt::QueuedConnection); |
| 4689 | 4930 | } |
| 4690 | 4931 | |
| 4932 | +void Application::loadAvatarScripts(const QVector<QString>& urls) { | |
| 4933 | + auto scriptEngines = DependencyManager::get<ScriptEngines>(); | |
| 4934 | + auto runningScripts = scriptEngines->getRunningScripts(); | |
| 4935 | + for (auto url : urls) { | |
| 4936 | + int index = runningScripts.indexOf(url); | |
| 4937 | + if (index < 0) { | |
| 4938 | + auto scriptEnginePointer = scriptEngines->loadScript(url, false); | |
| 4939 | + if (scriptEnginePointer) { | |
| 4940 | + scriptEnginePointer->setType(ScriptEngine::Type::AVATAR); | |
| 4941 | + } | |
| 4942 | + } | |
| 4943 | + } | |
| 4944 | +} | |
| 4945 | + | |
| 4946 | +void Application::unloadAvatarScripts() { | |
| 4947 | + auto scriptEngines = DependencyManager::get<ScriptEngines>(); | |
| 4948 | + auto urls = scriptEngines->getRunningScripts(); | |
| 4949 | + for (auto url : urls) { | |
| 4950 | + auto scriptEngine = scriptEngines->getScriptEngine(url); | |
| 4951 | + if (scriptEngine->getType() == ScriptEngine::Type::AVATAR) { | |
| 4952 | + scriptEngines->stopScript(url, false); | |
| 4953 | + } | |
| 4954 | + } | |
| 4955 | +} | |
| 4956 | + | |
| 4691 | 4957 | void Application::updateLOD(float deltaTime) const { |
| 4692 | 4958 | PerformanceTimer perfTimer("LOD"); |
| 4693 | 4959 | // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode |
| @@ -4703,7 +4969,7 @@ void Application::updateLOD(float deltaTime) const { · 9 unmodified lines | ||
| 4703 | 4969 | } |
| 4704 | 4970 | } |
| 4705 | 4971 | |
| 4706 | −void Application::pushPostUpdateLambda(void* key, std::function<void()> func) { | |
| 4972 | +void Application::pushPostUpdateLambda(void* key, const std::function<void()>& func) { | |
| 4707 | 4973 | std::unique_lock<std::mutex> guard(_postUpdateLambdasLock); |
| 4708 | 4974 | _postUpdateLambdas[key] = func; |
| 4709 | 4975 | } |
| @@ -4819,12 +5085,12 @@ void Application::updateThreads(float deltaTime) { · 109 unmodified lines | ||
| 4819 | 5085 | |
| 4820 | 5086 | void Application::toggleOverlays() { |
| 4821 | 5087 | auto menu = Menu::getInstance(); |
| 4822 | − menu->setIsOptionChecked(MenuOption::Overlays, menu->isOptionChecked(MenuOption::Overlays)); | |
| 5088 | + menu->setIsOptionChecked(MenuOption::Overlays, !menu->isOptionChecked(MenuOption::Overlays)); | |
| 4823 | 5089 | } |
| 4824 | 5090 | |
| 4825 | 5091 | void Application::setOverlaysVisible(bool visible) { |
| 4826 | 5092 | auto menu = Menu::getInstance(); |
| 4827 | − menu->setIsOptionChecked(MenuOption::Overlays, true); | |
| 5093 | + menu->setIsOptionChecked(MenuOption::Overlays, visible); | |
| 4828 | 5094 | } |
| 4829 | 5095 | |
| 4830 | 5096 | void Application::centerUI() { |
| @@ -4878,6 +5144,21 @@ void Application::cameraModeChanged() { · 47 unmodified lines | ||
| 4878 | 5144 | cameraMenuChanged(); |
| 4879 | 5145 | } |
| 4880 | 5146 | |
| 5147 | +void Application::changeViewAsNeeded(float boomLength) { | |
| 5148 | + // Switch between first and third person views as needed | |
| 5149 | + // This is called when the boom length has changed | |
| 5150 | + bool boomLengthGreaterThanMinimum = (boomLength > MyAvatar::ZOOM_MIN); | |
| 5151 | + | |
| 5152 | + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON && boomLengthGreaterThanMinimum) { | |
| 5153 | + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false); | |
| 5154 | + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); | |
| 5155 | + cameraMenuChanged(); | |
| 5156 | + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON && !boomLengthGreaterThanMinimum) { | |
| 5157 | + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); | |
| 5158 | + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, false); | |
| 5159 | + cameraMenuChanged(); | |
| 5160 | + } | |
| 5161 | +} | |
| 4881 | 5162 | |
| 4882 | 5163 | void Application::cameraMenuChanged() { |
| 4883 | 5164 | auto menu = Menu::getInstance(); |
| @@ -4926,7 +5207,7 @@ void Application::reloadResourceCaches() { · 42 unmodified lines | ||
| 4926 | 5207 | resetPhysicsReadyInformation(); |
| 4927 | 5208 | |
| 4928 | 5209 | // Query the octree to refresh everything in view |
| 4929 | − _lastQueriedTime = 0; | |
| 5210 | + _queryExpiry = SteadyClock::now(); | |
| 4930 | 5211 | _octreeQuery.incrementConnectionID(); |
| 4931 | 5212 | |
| 4932 | 5213 | queryOctree(NodeType::EntityServer, PacketType::EntityQuery); |
| @@ -5060,6 +5341,78 @@ void Application::updateDialogs(float deltaTime) const { · 127 unmodified lines | ||
| 5060 | 5341 | } |
| 5061 | 5342 | } |
| 5062 | 5343 | |
| 5344 | +void Application::updateSecondaryCameraViewFrustum() { | |
| 5345 | + // TODO: Fix this by modeling the way the secondary camera works on how the main camera works | |
| 5346 | + // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary | |
| 5347 | + // camera should be. | |
| 5348 | + | |
| 5349 | + // Code based on SecondaryCameraJob | |
| 5350 | + auto renderConfig = _renderEngine->getConfiguration(); | |
| 5351 | + assert(renderConfig); | |
| 5352 | + auto camera = dynamic_cast<SecondaryCameraJobConfig*>(renderConfig->getConfig("SecondaryCamera")); | |
| 5353 | + | |
| 5354 | + if (!camera || !camera->isEnabled()) { | |
| 5355 | + return; | |
| 5356 | + } | |
| 5357 | + | |
| 5358 | + ViewFrustum secondaryViewFrustum; | |
| 5359 | + if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) { | |
| 5360 | + auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 5361 | + auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId); | |
| 5362 | + glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition(); | |
| 5363 | + glm::quat mirrorPropertiesRotation = entityProperties.getRotation(); | |
| 5364 | + glm::vec3 mirrorPropertiesDimensions = entityProperties.getDimensions(); | |
| 5365 | + glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions; | |
| 5366 | + | |
| 5367 | + // setup mirror from world as inverse of world from mirror transformation using inverted x and z for mirrored image | |
| 5368 | + // TODO: we are assuming here that UP is world y-axis | |
| 5369 | + glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation) * glm::scale(vec3(-1.0f, 1.0f, -1.0f)); | |
| 5370 | + glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition); | |
| 5371 | + glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation; | |
| 5372 | + glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror); | |
| 5373 | + | |
| 5374 | + // get mirror camera position by reflecting main camera position's z coordinate in mirror space | |
| 5375 | + glm::vec3 mainCameraPositionWorld = getCamera().getPosition(); | |
| 5376 | + glm::vec3 mainCameraPositionMirror = vec3(mirrorFromWorld * vec4(mainCameraPositionWorld, 1.0f)); | |
| 5377 | + glm::vec3 mirrorCameraPositionMirror = vec3(mainCameraPositionMirror.x, mainCameraPositionMirror.y, | |
| 5378 | + -mainCameraPositionMirror.z); | |
| 5379 | + glm::vec3 mirrorCameraPositionWorld = vec3(worldFromMirror * vec4(mirrorCameraPositionMirror, 1.0f)); | |
| 5380 | + | |
| 5381 | + // set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation | |
| 5382 | + glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation); | |
| 5383 | + secondaryViewFrustum.setPosition(mirrorCameraPositionWorld); | |
| 5384 | + secondaryViewFrustum.setOrientation(mirrorCameraOrientation); | |
| 5385 | + | |
| 5386 | + // build frustum using mirror space translation of mirrored camera | |
| 5387 | + float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f; | |
| 5388 | + glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror; | |
| 5389 | + glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror; | |
| 5390 | + glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, camera->farClipPlaneDistance); | |
| 5391 | + secondaryViewFrustum.setProjection(frustum); | |
| 5392 | + } else { | |
| 5393 | + if (!camera->attachedEntityId.isNull()) { | |
| 5394 | + auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 5395 | + auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId); | |
| 5396 | + secondaryViewFrustum.setPosition(entityProperties.getPosition()); | |
| 5397 | + secondaryViewFrustum.setOrientation(entityProperties.getRotation()); | |
| 5398 | + } else { | |
| 5399 | + secondaryViewFrustum.setPosition(camera->position); | |
| 5400 | + secondaryViewFrustum.setOrientation(camera->orientation); | |
| 5401 | + } | |
| 5402 | + | |
| 5403 | + float aspectRatio = (float)camera->textureWidth / (float)camera->textureHeight; | |
| 5404 | + secondaryViewFrustum.setProjection(camera->vFoV, | |
| 5405 | + aspectRatio, | |
| 5406 | + camera->nearClipPlaneDistance, | |
| 5407 | + camera->farClipPlaneDistance); | |
| 5408 | + } | |
| 5409 | + // Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera, | |
| 5410 | + // which is not what we want here. | |
| 5411 | + secondaryViewFrustum.calculate(); | |
| 5412 | + | |
| 5413 | + _conicalViews.push_back(secondaryViewFrustum); | |
| 5414 | +} | |
| 5415 | + | |
| 5063 | 5416 | static bool domainLoadingInProgress = false; |
| 5064 | 5417 | |
| 5065 | 5418 | void Application::update(float deltaTime) { |
| @@ -5084,7 +5437,7 @@ void Application::update(float deltaTime) { · 18 unmodified lines | ||
| 5084 | 5437 | // process octree stats packets are sent in between full sends of a scene (this isn't currently true). |
| 5085 | 5438 | // We keep physics disabled until we've received a full scene and everything near the avatar in that |
| 5086 | 5439 | // scene is ready to compute its collision shape. |
| 5087 | − if (nearbyEntitiesAreReadyForPhysics()) { | |
| 5440 | + if (nearbyEntitiesAreReadyForPhysics() && getMyAvatar()->isReadyForPhysics()) { | |
| 5088 | 5441 | _physicsEnabled = true; |
| 5089 | 5442 | getMyAvatar()->updateMotionBehaviorFromMenu(); |
| 5090 | 5443 | } |
| @@ -5117,7 +5470,7 @@ void Application::update(float deltaTime) { · 26 unmodified lines | ||
| 5117 | 5470 | if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !audioClient->isMuted()) { |
| 5118 | 5471 | if (_lastFaceTrackerUpdate > 0 |
| 5119 | 5472 | && ((usecTimestampNow() - _lastFaceTrackerUpdate) > MUTE_MICROPHONE_AFTER_USECS)) { |
| 5120 | − audioClient->toggleMute(); | |
| 5473 | + audioClient->setMuted(true); | |
| 5121 | 5474 | _lastFaceTrackerUpdate = 0; |
| 5122 | 5475 | } |
| 5123 | 5476 | } else { |
| @@ -5258,12 +5611,12 @@ void Application::update(float deltaTime) { · 134 unmodified lines | ||
| 5258 | 5611 | QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>(); |
| 5259 | 5612 | |
| 5260 | 5613 | { |
| 5261 | − PROFILE_RANGE(simulation_physics, "Physics"); | |
| 5262 | − PerformanceTimer perfTimer("physics"); | |
| 5614 | + PROFILE_RANGE(simulation_physics, "Simulation"); | |
| 5615 | + PerformanceTimer perfTimer("simulation"); | |
| 5263 | 5616 | if (_physicsEnabled) { |
| 5264 | 5617 | { |
| 5265 | − PROFILE_RANGE(simulation_physics, "PreStep"); | |
| 5266 | − PerformanceTimer perfTimer("preStep)"); | |
| 5618 | + PROFILE_RANGE(simulation_physics, "PrePhysics"); | |
| 5619 | + PerformanceTimer perfTimer("prePhysics)"); | |
| 5267 | 5620 | { |
| 5268 | 5621 | const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); |
| 5269 | 5622 | _physicsEngine->removeObjects(motionStates); |
| @@ -5297,59 +5650,63 @@ void Application::update(float deltaTime) { · 27 unmodified lines | ||
| 5297 | 5650 | }); |
| 5298 | 5651 | } |
| 5299 | 5652 | { |
| 5300 | − PROFILE_RANGE(simulation_physics, "Step"); | |
| 5301 | − PerformanceTimer perfTimer("step"); | |
| 5653 | + PROFILE_RANGE(simulation_physics, "StepPhysics"); | |
| 5654 | + PerformanceTimer perfTimer("stepPhysics"); | |
| 5302 | 5655 | getEntities()->getTree()->withWriteLock([&] { |
| 5303 | 5656 | _physicsEngine->stepSimulation(); |
| 5304 | 5657 | }); |
| 5305 | 5658 | } |
| 5306 | 5659 | { |
| 5307 | − PROFILE_RANGE(simulation_physics, "PostStep"); | |
| 5308 | − PerformanceTimer perfTimer("postStep"); | |
| 5309 | 5660 | if (_physicsEngine->hasOutgoingChanges()) { |
| 5310 | − // grab the collision events BEFORE handleOutgoingChanges() because at this point | |
| 5311 | − // we have a better idea of which objects we own or should own. | |
| 5312 | − auto& collisionEvents = _physicsEngine->getCollisionEvents(); | |
| 5313 | − | |
| 5314 | − getEntities()->getTree()->withWriteLock([&] { | |
| 5315 | − PROFILE_RANGE(simulation_physics, "HandleChanges"); | |
| 5316 | − PerformanceTimer perfTimer("handleChanges"); | |
| 5317 | − | |
| 5318 | − const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); | |
| 5319 | − _entitySimulation->handleChangedMotionStates(outgoingChanges); | |
| 5320 | − avatarManager->handleChangedMotionStates(outgoingChanges); | |
| 5321 | − | |
| 5322 | − const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); | |
| 5323 | − _entitySimulation->handleDeactivatedMotionStates(deactivations); | |
| 5324 | − }); | |
| 5661 | + { | |
| 5662 | + PROFILE_RANGE(simulation_physics, "PostPhysics"); | |
| 5663 | + PerformanceTimer perfTimer("postPhysics"); | |
| 5664 | + // grab the collision events BEFORE handleChangedMotionStates() because at this point | |
| 5665 | + // we have a better idea of which objects we own or should own. | |
| 5666 | + auto& collisionEvents = _physicsEngine->getCollisionEvents(); | |
| 5667 | + | |
| 5668 | + getEntities()->getTree()->withWriteLock([&] { | |
| 5669 | + PROFILE_RANGE(simulation_physics, "HandleChanges"); | |
| 5670 | + PerformanceTimer perfTimer("handleChanges"); | |
| 5671 | + | |
| 5672 | + const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); | |
| 5673 | + _entitySimulation->handleChangedMotionStates(outgoingChanges); | |
| 5674 | + avatarManager->handleChangedMotionStates(outgoingChanges); | |
| 5675 | + | |
| 5676 | + const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); | |
| 5677 | + _entitySimulation->handleDeactivatedMotionStates(deactivations); | |
| 5678 | + }); | |
| 5679 | + | |
| 5680 | + if (!_aboutToQuit) { | |
| 5681 | + // handleCollisionEvents() AFTER handleChangedMotionStates() | |
| 5682 | + { | |
| 5683 | + PROFILE_RANGE(simulation_physics, "CollisionEvents"); | |
| 5684 | + avatarManager->handleCollisionEvents(collisionEvents); | |
| 5685 | + // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk | |
| 5686 | + // deadlock.) | |
| 5687 | + _entitySimulation->handleCollisionEvents(collisionEvents); | |
| 5688 | + } | |
| 5689 | + } | |
| 5325 | 5690 | |
| 5326 | − if (!_aboutToQuit) { | |
| 5327 | − // handleCollisionEvents() AFTER handleOutgoingChanges() | |
| 5328 | 5691 | { |
| 5329 | − PROFILE_RANGE(simulation_physics, "CollisionEvents"); | |
| 5330 | − avatarManager->handleCollisionEvents(collisionEvents); | |
| 5331 | − // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk | |
| 5332 | − // deadlock.) | |
| 5333 | − _entitySimulation->handleCollisionEvents(collisionEvents); | |
| 5692 | + PROFILE_RANGE(simulation_physics, "MyAvatar"); | |
| 5693 | + myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); | |
| 5334 | 5694 | } |
| 5335 | 5695 | |
| 5336 | − // NOTE: the getEntities()->update() call below will wait for lock | |
| 5337 | − // and will simulate entity motion (the EntityTree has been given an EntitySimulation). | |
| 5338 | − getEntities()->update(true); // update the models... | |
| 5339 | − } | |
| 5340 | − | |
| 5341 | − { | |
| 5342 | − PROFILE_RANGE(simulation_physics, "MyAvatar"); | |
| 5343 | − myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); | |
| 5696 | + if (PerformanceTimer::isActive() && | |
| 5697 | + Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && | |
| 5698 | + Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsTiming)) { | |
| 5699 | + _physicsEngine->harvestPerformanceStats(); | |
| 5700 | + } | |
| 5701 | + // NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework | |
| 5702 | + _physicsEngine->dumpStatsIfNecessary(); | |
| 5344 | 5703 | } |
| 5345 | 5704 | |
| 5346 | − if (PerformanceTimer::isActive() && | |
| 5347 | − Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && | |
| 5348 | − Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) { | |
| 5349 | − _physicsEngine->harvestPerformanceStats(); | |
| 5705 | + if (!_aboutToQuit) { | |
| 5706 | + // NOTE: the getEntities()->update() call below will wait for lock | |
| 5707 | + // and will provide non-physical entity motion | |
| 5708 | + getEntities()->update(true); // update the models... | |
| 5350 | 5709 | } |
| 5351 | − // NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework | |
| 5352 | − _physicsEngine->dumpStatsIfNecessary(); | |
| 5353 | 5710 | } |
| 5354 | 5711 | } |
| 5355 | 5712 | } else { |
| @@ -5409,6 +5766,13 @@ void Application::update(float deltaTime) { · 53 unmodified lines | ||
| 5409 | 5766 | { |
| 5410 | 5767 | QMutexLocker viewLocker(&_viewMutex); |
| 5411 | 5768 | _myCamera.loadViewFrustum(_viewFrustum); |
| 5769 | + | |
| 5770 | + _conicalViews.clear(); | |
| 5771 | + _conicalViews.push_back(_viewFrustum); | |
| 5772 | + // TODO: Fix this by modeling the way the secondary camera works on how the main camera works | |
| 5773 | + // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary | |
| 5774 | + // camera should be. | |
| 5775 | + updateSecondaryCameraViewFrustum(); | |
| 5412 | 5776 | } |
| 5413 | 5777 | |
| 5414 | 5778 | quint64 now = usecTimestampNow(); |
| @@ -5418,18 +5782,31 @@ void Application::update(float deltaTime) { · 3 unmodified lines | ||
| 5418 | 5782 | PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); |
| 5419 | 5783 | PerformanceTimer perfTimer("queryOctree"); |
| 5420 | 5784 | QMutexLocker viewLocker(&_viewMutex); |
| 5421 | − quint64 sinceLastQuery = now - _lastQueriedTime; | |
| 5422 | − const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; | |
| 5423 | − bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY; | |
| 5424 | − bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum); | |
| 5785 | + | |
| 5786 | + bool viewIsDifferentEnough = false; | |
| 5787 | + if (_conicalViews.size() == _lastQueriedViews.size()) { | |
| 5788 | + for (size_t i = 0; i < _conicalViews.size(); ++i) { | |
| 5789 | + if (!_conicalViews[i].isVerySimilar(_lastQueriedViews[i])) { | |
| 5790 | + viewIsDifferentEnough = true; | |
| 5791 | + break; | |
| 5792 | + } | |
| 5793 | + } | |
| 5794 | + } else { | |
| 5795 | + viewIsDifferentEnough = true; | |
| 5796 | + } | |
| 5797 | + | |
| 5798 | + | |
| 5425 | 5799 | // if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it |
| 5426 | − if (queryIsDue || viewIsDifferentEnough) { | |
| 5427 | − _lastQueriedTime = now; | |
| 5800 | + static const std::chrono::seconds MIN_PERIOD_BETWEEN_QUERIES { 3 }; | |
| 5801 | + auto now = SteadyClock::now(); | |
| 5802 | + if (now > _queryExpiry || viewIsDifferentEnough) { | |
| 5428 | 5803 | if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) { |
| 5429 | 5804 | queryOctree(NodeType::EntityServer, PacketType::EntityQuery); |
| 5430 | 5805 | } |
| 5431 | − sendAvatarViewFrustum(); | |
| 5432 | − _lastQueriedViewFrustum = _viewFrustum; | |
| 5806 | + queryAvatars(); | |
| 5807 | + | |
| 5808 | + _lastQueriedViews = _conicalViews; | |
| 5809 | + _queryExpiry = now + MIN_PERIOD_BETWEEN_QUERIES; | |
| 5433 | 5810 | } |
| 5434 | 5811 | } |
| 5435 | 5812 | |
| @@ -5469,6 +5846,28 @@ void Application::update(float deltaTime) { · 33 unmodified lines | ||
| 5469 | 5846 | } |
| 5470 | 5847 | |
| 5471 | 5848 | |
| 5849 | + updateRenderArgs(deltaTime); | |
| 5850 | + | |
| 5851 | + // HACK | |
| 5852 | + // load the view frustum | |
| 5853 | + // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. | |
| 5854 | + // Then we can move this logic into the Avatar::simulate call. | |
| 5855 | + myAvatar->preDisplaySide(&_appRenderArgs._renderArgs); | |
| 5856 | + | |
| 5857 | + | |
| 5858 | + { | |
| 5859 | + PerformanceTimer perfTimer("AnimDebugDraw"); | |
| 5860 | + AnimDebugDraw::getInstance().update(); | |
| 5861 | + } | |
| 5862 | + | |
| 5863 | + | |
| 5864 | + { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over | |
| 5865 | + PerformanceTimer perfTimer("enqueueFrame"); | |
| 5866 | + getMain3DScene()->enqueueFrame(); | |
| 5867 | + } | |
| 5868 | +} | |
| 5869 | + | |
| 5870 | +void Application::updateRenderArgs(float deltaTime) { | |
| 5472 | 5871 | editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { |
| 5473 | 5872 | PerformanceTimer perfTimer("editRenderArgs"); |
| 5474 | 5873 | appRenderArgs._headPose = getHMDSensorPose(); |
| @@ -5492,9 +5891,9 @@ void Application::update(float deltaTime) { · 17 unmodified lines | ||
| 5492 | 5891 | QMutexLocker viewLocker(&_viewMutex); |
| 5493 | 5892 | // adjust near clip plane to account for sensor scaling. |
| 5494 | 5893 | auto adjustedProjection = glm::perspective(glm::radians(_fieldOfView.get()), |
| 5495 | − getActiveDisplayPlugin()->getRecommendedAspectRatio(), | |
| 5496 | − DEFAULT_NEAR_CLIP * sensorToWorldScale, | |
| 5497 | − DEFAULT_FAR_CLIP); | |
| 5894 | + getActiveDisplayPlugin()->getRecommendedAspectRatio(), | |
| 5895 | + DEFAULT_NEAR_CLIP * sensorToWorldScale, | |
| 5896 | + DEFAULT_FAR_CLIP); | |
| 5498 | 5897 | _viewFrustum.setProjection(adjustedProjection); |
| 5499 | 5898 | _viewFrustum.calculate(); |
| 5500 | 5899 | } |
| @@ -5510,8 +5909,14 @@ void Application::update(float deltaTime) { · 9 unmodified lines | ||
| 5510 | 5909 | } |
| 5511 | 5910 | { |
| 5512 | 5911 | PROFILE_RANGE(render, "/resizeGL"); |
| 5513 | − PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); | |
| 5514 | − bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); | |
| 5912 | + bool showWarnings = false; | |
| 5913 | + bool suppressShortTimings = false; | |
| 5914 | + auto menu = Menu::getInstance(); | |
| 5915 | + if (menu) { | |
| 5916 | + suppressShortTimings = menu->isOptionChecked(MenuOption::SuppressShortTimings); | |
| 5917 | + showWarnings = menu->isOptionChecked(MenuOption::PipelineWarnings); | |
| 5918 | + } | |
| 5919 | + PerformanceWarning::setSuppressShortTimings(suppressShortTimings); | |
| 5515 | 5920 | PerformanceWarning warn(showWarnings, "Application::paintGL()"); |
| 5516 | 5921 | resizeGL(); |
| 5517 | 5922 | } |
| @@ -5567,12 +5972,6 @@ void Application::update(float deltaTime) { · 49 unmodified lines | ||
| 5567 | 5972 | } |
| 5568 | 5973 | } |
| 5569 | 5974 | |
| 5570 | − // HACK | |
| 5571 | − // load the view frustum | |
| 5572 | − // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. | |
| 5573 | − // Then we can move this logic into the Avatar::simulate call. | |
| 5574 | − myAvatar->preDisplaySide(&appRenderArgs._renderArgs); | |
| 5575 | − | |
| 5576 | 5975 | { |
| 5577 | 5976 | QMutexLocker viewLocker(&_viewMutex); |
| 5578 | 5977 | _myCamera.loadViewFrustum(_displayViewFrustum); |
| @@ -5584,22 +5983,22 @@ void Application::update(float deltaTime) { · 5 unmodified lines | ||
| 5584 | 5983 | appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum); |
| 5585 | 5984 | } |
| 5586 | 5985 | }); |
| 5986 | +} | |
| 5587 | 5987 | |
| 5588 | − { | |
| 5589 | − PerformanceTimer perfTimer("AnimDebugDraw"); | |
| 5590 | − AnimDebugDraw::getInstance().update(); | |
| 5591 | − } | |
| 5988 | +void Application::queryAvatars() { | |
| 5989 | + auto avatarPacket = NLPacket::create(PacketType::AvatarQuery); | |
| 5990 | + auto destinationBuffer = reinterpret_cast<unsigned char*>(avatarPacket->getPayload()); | |
| 5991 | + unsigned char* bufferStart = destinationBuffer; | |
| 5592 | 5992 | |
| 5593 | − { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over | |
| 5594 | − PerformanceTimer perfTimer("enqueueFrame"); | |
| 5595 | − getMain3DScene()->enqueueFrame(); | |
| 5993 | + uint8_t numFrustums = (uint8_t)_conicalViews.size(); | |
| 5994 | + memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums)); | |
| 5995 | + destinationBuffer += sizeof(numFrustums); | |
| 5996 | + | |
| 5997 | + for (const auto& view : _conicalViews) { | |
| 5998 | + destinationBuffer += view.serialize(destinationBuffer); | |
| 5596 | 5999 | } |
| 5597 | −} | |
| 5598 | 6000 | |
| 5599 | −void Application::sendAvatarViewFrustum() { | |
| 5600 | − QByteArray viewFrustumByteArray = _viewFrustum.toByteArray(); | |
| 5601 | − auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size()); | |
| 5602 | − avatarPacket->write(viewFrustumByteArray); | |
| 6001 | + avatarPacket->setPayloadSize(destinationBuffer - bufferStart); | |
| 5603 | 6002 | |
| 5604 | 6003 | DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer); |
| 5605 | 6004 | } |
| @@ -5661,16 +6060,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { · 55 unmodified lines | ||
| 5661 | 6060 | return; // bail early if settings are not loaded |
| 5662 | 6061 | } |
| 5663 | 6062 | |
| 5664 | − ViewFrustum viewFrustum; | |
| 5665 | − copyViewFrustum(viewFrustum); | |
| 5666 | − _octreeQuery.setCameraPosition(viewFrustum.getPosition()); | |
| 5667 | − _octreeQuery.setCameraOrientation(viewFrustum.getOrientation()); | |
| 5668 | − _octreeQuery.setCameraFov(viewFrustum.getFieldOfView()); | |
| 5669 | − _octreeQuery.setCameraAspectRatio(viewFrustum.getAspectRatio()); | |
| 5670 | − _octreeQuery.setCameraNearClip(viewFrustum.getNearClip()); | |
| 5671 | − _octreeQuery.setCameraFarClip(viewFrustum.getFarClip()); | |
| 5672 | − _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); | |
| 5673 | − _octreeQuery.setCameraCenterRadius(viewFrustum.getCenterRadius()); | |
| 6063 | + _octreeQuery.setConicalViews(_conicalViews); | |
| 6064 | + | |
| 5674 | 6065 | auto lodManager = DependencyManager::get<LODManager>(); |
| 5675 | 6066 | _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); |
| 5676 | 6067 | _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); |
| @@ -5768,7 +6159,9 @@ void Application::updateWindowTitle() const { · 91 unmodified lines | ||
| 5768 | 6159 | auto nodeList = DependencyManager::get<NodeList>(); |
| 5769 | 6160 | auto accountManager = DependencyManager::get<AccountManager>(); |
| 5770 | 6161 | |
| 5771 | − QString buildVersion = " (build " + applicationVersion() + ")"; | |
| 6162 | + QString buildVersion = " - " | |
| 6163 | + + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) | |
| 6164 | + + " " + applicationVersion(); | |
| 5772 | 6165 | |
| 5773 | 6166 | QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)"; |
| 5774 | 6167 | |
| @@ -5879,7 +6272,7 @@ void Application::nodeActivated(SharedNodePointer node) { · 104 unmodified lines | ||
| 5879 | 6272 | // If we get a new EntityServer activated, reset lastQueried time |
| 5880 | 6273 | // so we will do a proper query during update |
| 5881 | 6274 | if (node->getType() == NodeType::EntityServer) { |
| 5882 | − _lastQueriedTime = 0; | |
| 6275 | + _queryExpiry = SteadyClock::now(); | |
| 5883 | 6276 | _octreeQuery.incrementConnectionID(); |
| 5884 | 6277 | } |
| 5885 | 6278 | |
| @@ -5888,6 +6281,8 @@ void Application::nodeActivated(SharedNodePointer node) { · 2 unmodified lines | ||
| 5888 | 6281 | } |
| 5889 | 6282 | |
| 5890 | 6283 | if (node->getType() == NodeType::AvatarMixer) { |
| 6284 | + _queryExpiry = SteadyClock::now(); | |
| 6285 | + | |
| 5891 | 6286 | // new avatar mixer, send off our identity packet on next update loop |
| 5892 | 6287 | // Reset skeletonModelUrl if the last server modified our choice. |
| 5893 | 6288 | // Override the avatar url (but not model name) here too. |
| @@ -5900,6 +6295,9 @@ void Application::nodeActivated(SharedNodePointer node) { · 6 unmodified lines | ||
| 5900 | 6295 | } |
| 5901 | 6296 | getMyAvatar()->markIdentityDataChanged(); |
| 5902 | 6297 | getMyAvatar()->resetLastSent(); |
| 6298 | + | |
| 6299 | + // transmit a "sendAll" packet to the AvatarMixer we just connected to. | |
| 6300 | + getMyAvatar()->sendAvatarDataPacket(true); | |
| 5903 | 6301 | } |
| 5904 | 6302 | } |
| 5905 | 6303 | |
| @@ -6005,9 +6403,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() { · 99 unmodified lines | ||
| 6005 | 6403 | bool result = true; |
| 6006 | 6404 | foreach (EntityItemPointer entity, entities) { |
| 6007 | 6405 | if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) { |
| 6008 | − static QString repeatedMessage = | |
| 6009 | − LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*"); | |
| 6010 | − qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName(); | |
| 6406 | + HIFI_FCDEBUG(interfaceapp(), "Physics disabled until entity loads: " << entity->getID() << entity->getName()); | |
| 6011 | 6407 | // don't break here because we want all the relevant entities to start their downloads |
| 6012 | 6408 | result = false; |
| 6013 | 6409 | } |
| @@ -6191,8 +6587,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe · 177 unmodified lines | ||
| 6191 | 6587 | scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data()); |
| 6192 | 6588 | scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data()); |
| 6193 | 6589 | scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data()); |
| 6194 | − | |
| 6195 | − scriptEngine->registerGlobalObject("App", this); | |
| 6590 | + scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); | |
| 6196 | 6591 | |
| 6197 | 6592 | qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue); |
| 6198 | 6593 | |
| @@ -6510,12 +6905,7 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const · 311 unmodified lines | ||
| 6510 | 6905 | if (onTablet) { |
| 6511 | 6906 | toggleTabletUI(true); |
| 6512 | 6907 | } |
| 6513 | − } | |
| 6514 | − | |
| 6515 | − if (!onTablet) { | |
| 6516 | − DependencyManager::get<OffscreenUi>()->show(widgetUrl, name); | |
| 6517 | − } | |
| 6518 | − if (tablet->getToolbarMode()) { | |
| 6908 | + } else { | |
| 6519 | 6909 | DependencyManager::get<OffscreenUi>()->show(widgetUrl, name); |
| 6520 | 6910 | } |
| 6521 | 6911 | } |
| @@ -6569,7 +6959,7 @@ void Application::addAssetToWorldFromURL(QString url) { · 47 unmodified lines | ||
| 6569 | 6959 | } else { |
| 6570 | 6960 | filename.remove(".zip"); |
| 6571 | 6961 | } |
| 6572 | − | |
| 6962 | + | |
| 6573 | 6963 | } |
| 6574 | 6964 | |
| 6575 | 6965 | if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) { |
| @@ -6743,7 +7133,7 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q · 167 unmodified lines | ||
| 6743 | 7133 | addAssetToWorldError(filenameFromPath(filePath), errorInfo); |
| 6744 | 7134 | } else { |
| 6745 | 7135 | // to prevent files that aren't models or texture files from being loaded into world automatically |
| 6746 | − if ((filePath.toLower().endsWith(OBJ_EXTENSION) || filePath.toLower().endsWith(FBX_EXTENSION)) || | |
| 7136 | + if ((filePath.toLower().endsWith(OBJ_EXTENSION) || filePath.toLower().endsWith(FBX_EXTENSION)) || | |
| 6747 | 7137 | ((filePath.toLower().endsWith(JPG_EXTENSION) || filePath.toLower().endsWith(PNG_EXTENSION)) && |
| 6748 | 7138 | ((!isBlocks) && (!isZip)))) { |
| 6749 | 7139 | addAssetToWorldAddEntity(filePath, mapping); |
| @@ -7137,7 +7527,6 @@ void Application::loadLODToolsDialog() { · 387 unmodified lines | ||
| 7137 | 7527 | } |
| 7138 | 7528 | } |
| 7139 | 7529 | |
| 7140 | − | |
| 7141 | 7530 | void Application::loadEntityStatisticsDialog() { |
| 7142 | 7531 | auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); |
| 7143 | 7532 | auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); |
| @@ -7201,11 +7590,15 @@ void Application::loadAvatarBrowser() const { · 57 unmodified lines | ||
| 7201 | 7590 | void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { |
| 7202 | 7591 | postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] { |
| 7203 | 7592 | // Get a screenshot and save it |
| 7204 | − QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename); | |
| 7593 | + QString path = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, | |
| 7594 | + TestScriptingInterface::getInstance()->getTestResultsLocation()); | |
| 7595 | + | |
| 7205 | 7596 | // If we're not doing an animated snapshot as well... |
| 7206 | 7597 | if (!includeAnimated) { |
| 7207 | − // Tell the dependency manager that the capture of the still snapshot has taken place. | |
| 7208 | − emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify); | |
| 7598 | + if (!path.isEmpty()) { | |
| 7599 | + // Tell the dependency manager that the capture of the still snapshot has taken place. | |
| 7600 | + emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify); | |
| 7601 | + } | |
| 7209 | 7602 | } else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) { |
| 7210 | 7603 | // Get an animated GIF snapshot and save it |
| 7211 | 7604 | SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>()); |
| @@ -7213,30 +7606,42 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa · 1 unmodified line | ||
| 7213 | 7606 | }); |
| 7214 | 7607 | } |
| 7215 | 7608 | |
| 7216 | −void Application::takeSecondaryCameraSnapshot(const QString& filename) { | |
| 7217 | − postLambdaEvent([filename, this] { | |
| 7218 | − QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename); | |
| 7219 | − emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true); | |
| 7609 | +void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) { | |
| 7610 | + postLambdaEvent([notify, filename, this] { | |
| 7611 | + QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, | |
| 7612 | + TestScriptingInterface::getInstance()->getTestResultsLocation()); | |
| 7613 | + | |
| 7614 | + emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, notify); | |
| 7615 | + }); | |
| 7616 | +} | |
| 7617 | + | |
| 7618 | +void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) { | |
| 7619 | + postLambdaEvent([notify, filename, cubemapOutputFormat, cameraPosition] { | |
| 7620 | + DependencyManager::get<Snapshot>()->save360Snapshot(cameraPosition, cubemapOutputFormat, notify, filename); | |
| 7220 | 7621 | }); |
| 7221 | 7622 | } |
| 7222 | 7623 | |
| 7223 | 7624 | void Application::shareSnapshot(const QString& path, const QUrl& href) { |
| 7224 | 7625 | postLambdaEvent([path, href] { |
| 7225 | 7626 | // not much to do here, everything is done in snapshot code... |
| 7226 | − Snapshot::uploadSnapshot(path, href); | |
| 7627 | + DependencyManager::get<Snapshot>()->uploadSnapshot(path, href); | |
| 7227 | 7628 | }); |
| 7228 | 7629 | } |
| 7229 | 7630 | |
| 7230 | 7631 | float Application::getRenderResolutionScale() const { |
| 7231 | − if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { | |
| 7632 | + auto menu = Menu::getInstance(); | |
| 7633 | + if (!menu) { | |
| 7634 | + return 1.0f; | |
| 7635 | + } | |
| 7636 | + if (menu->isOptionChecked(MenuOption::RenderResolutionOne)) { | |
| 7232 | 7637 | return 1.0f; |
| 7233 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { | |
| 7638 | + } else if (menu->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { | |
| 7234 | 7639 | return 0.666f; |
| 7235 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) { | |
| 7640 | + } else if (menu->isOptionChecked(MenuOption::RenderResolutionHalf)) { | |
| 7236 | 7641 | return 0.5f; |
| 7237 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) { | |
| 7642 | + } else if (menu->isOptionChecked(MenuOption::RenderResolutionThird)) { | |
| 7238 | 7643 | return 0.333f; |
| 7239 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) { | |
| 7644 | + } else if (menu->isOptionChecked(MenuOption::RenderResolutionQuarter)) { | |
| 7240 | 7645 | return 0.25f; |
| 7241 | 7646 | } else { |
| 7242 | 7647 | return 1.0f; |
| @@ -7302,7 +7707,7 @@ void Application::windowMinimizedChanged(bool minimized) { · 59 unmodified lines | ||
| 7302 | 7707 | } |
| 7303 | 7708 | } |
| 7304 | 7709 | |
| 7305 | −void Application::postLambdaEvent(std::function<void()> f) { | |
| 7710 | +void Application::postLambdaEvent(const std::function<void()>& f) { | |
| 7306 | 7711 | if (this->thread() == QThread::currentThread()) { |
| 7307 | 7712 | f(); |
| 7308 | 7713 | } else { |
| @@ -7310,6 +7715,15 @@ void Application::postLambdaEvent(std::function<void()> f) { · 1 unmodified line | ||
| 7310 | 7715 | } |
| 7311 | 7716 | } |
| 7312 | 7717 | |
| 7718 | +void Application::sendLambdaEvent(const std::function<void()>& f) { | |
| 7719 | + if (this->thread() == QThread::currentThread()) { | |
| 7720 | + f(); | |
| 7721 | + } else { | |
| 7722 | + LambdaEvent event(f); | |
| 7723 | + QCoreApplication::sendEvent(this, &event); | |
| 7724 | + } | |
| 7725 | +} | |
| 7726 | + | |
| 7313 | 7727 | void Application::initPlugins(const QStringList& arguments) { |
| 7314 | 7728 | QCommandLineOption display("display", "Preferred displays", "displays"); |
| 7315 | 7729 | QCommandLineOption disableDisplays("disable-displays", "Displays to disable", "displays"); |
| @@ -7392,8 +7806,8 @@ bool Application::isThrottleRendering() const { · 76 unmodified lines | ||
| 7392 | 7806 | bool Application::hasFocus() const { |
| 7393 | 7807 | bool result = (QApplication::activeWindow() != nullptr); |
| 7394 | 7808 | #if defined(Q_OS_WIN) |
| 7395 | − // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't | |
| 7396 | − // take user focus away from their current window. So also check whether the application is the user's current foreground | |
| 7809 | + // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't | |
| 7810 | + // take user focus away from their current window. So also check whether the application is the user's current foreground | |
| 7397 | 7811 | // window. |
| 7398 | 7812 | result = result && (HWND)QApplication::activeWindow()->winId() == GetForegroundWindow(); |
| 7399 | 7813 | #endif |
| @@ -7401,7 +7815,7 @@ bool Application::hasFocus() const { · 1 unmodified line | ||
| 7401 | 7815 | } |
| 7402 | 7816 | |
| 7403 | 7817 | void Application::setFocus() { |
| 7404 | − // Note: Windows doesn't allow a user focus to be taken away from another application. Instead, it changes the color of and | |
| 7818 | + // Note: Windows doesn't allow a user focus to be taken away from another application. Instead, it changes the color of and | |
| 7405 | 7819 | // flashes the taskbar icon. |
| 7406 | 7820 | auto window = qApp->getWindow(); |
| 7407 | 7821 | window->activateWindow(); |
| @@ -7451,7 +7865,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const { · 43 unmodified lines | ||
| 7451 | 7865 | |
| 7452 | 7866 | static const char* EXCLUSION_GROUP_KEY = "exclusionGroup"; |
| 7453 | 7867 | |
| 7454 | −static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) { | |
| 7868 | +static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active) { | |
| 7455 | 7869 | auto menu = Menu::getInstance(); |
| 7456 | 7870 | QString name = displayPlugin->getName(); |
| 7457 | 7871 | auto grouping = displayPlugin->getGrouping(); |
| @@ -7478,7 +7892,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 20 unmodified lines | ||
| 7478 | 7892 | } |
| 7479 | 7893 | auto parent = menu->getMenu(MenuOption::OutputMenu); |
| 7480 | 7894 | auto action = menu->addActionToQMenuAndActionHash(parent, |
| 7481 | − name, 0, qApp, | |
| 7895 | + name, QKeySequence(Qt::CTRL + (Qt::Key_0 + index)), qApp, | |
| 7482 | 7896 | SLOT(updateDisplayMode()), |
| 7483 | 7897 | QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu); |
| 7484 | 7898 | |
| @@ -7496,66 +7910,24 @@ void Application::updateDisplayMode() { · 11 unmodified lines | ||
| 7496 | 7910 | qFatal("Attempted to switch display plugins from a non-main thread"); |
| 7497 | 7911 | } |
| 7498 | 7912 | |
| 7499 | − auto menu = Menu::getInstance(); | |
| 7500 | − auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); | |
| 7501 | − | |
| 7502 | − static std::once_flag once; | |
| 7503 | − std::call_once(once, [&] { | |
| 7504 | − bool first = true; | |
| 7505 | − | |
| 7506 | − // first sort the plugins into groupings: standard, advanced, developer | |
| 7507 | − DisplayPluginList standard; | |
| 7508 | − DisplayPluginList advanced; | |
| 7509 | − DisplayPluginList developer; | |
| 7510 | − foreach(auto displayPlugin, displayPlugins) { | |
| 7511 | − displayPlugin->setContext(_gpuContext); | |
| 7512 | − auto grouping = displayPlugin->getGrouping(); | |
| 7513 | − switch (grouping) { | |
| 7514 | − case Plugin::ADVANCED: | |
| 7515 | − advanced.push_back(displayPlugin); | |
| 7516 | − break; | |
| 7517 | − case Plugin::DEVELOPER: | |
| 7518 | − developer.push_back(displayPlugin); | |
| 7519 | − break; | |
| 7520 | − default: | |
| 7521 | − standard.push_back(displayPlugin); | |
| 7522 | − break; | |
| 7523 | − } | |
| 7524 | − } | |
| 7525 | − | |
| 7526 | − // concatenate the groupings into a single list in the order: standard, advanced, developer | |
| 7527 | − standard.insert(std::end(standard), std::begin(advanced), std::end(advanced)); | |
| 7528 | − standard.insert(std::end(standard), std::begin(developer), std::end(developer)); | |
| 7529 | − | |
| 7530 | − foreach(auto displayPlugin, standard) { | |
| 7531 | − addDisplayPluginToMenu(displayPlugin, first); | |
| 7532 | − auto displayPluginName = displayPlugin->getName(); | |
| 7533 | − QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { | |
| 7534 | − resizeGL(); | |
| 7535 | − }); | |
| 7536 | − QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); | |
| 7537 | − first = false; | |
| 7538 | − } | |
| 7539 | − | |
| 7540 | − // after all plugins have been added to the menu, add a separator to the menu | |
| 7541 | − auto menu = Menu::getInstance(); | |
| 7542 | − auto parent = menu->getMenu(MenuOption::OutputMenu); | |
| 7543 | − parent->addSeparator(); | |
| 7544 | − }); | |
| 7545 | − | |
| 7913 | + // Once time initialization code that depends on the UI being available | |
| 7914 | + auto displayPlugins = getDisplayPlugins(); | |
| 7546 | 7915 | |
| 7547 | 7916 | // Default to the first item on the list, in case none of the menu items match |
| 7548 | 7917 | DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0); |
| 7549 | − foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { | |
| 7550 | − QString name = displayPlugin->getName(); | |
| 7551 | − QAction* action = menu->getActionForOption(name); | |
| 7552 | − // Menu might have been removed if the display plugin lost | |
| 7553 | − if (!action) { | |
| 7554 | − continue; | |
| 7555 | − } | |
| 7556 | − if (action->isChecked()) { | |
| 7557 | − newDisplayPlugin = displayPlugin; | |
| 7558 | − break; | |
| 7918 | + auto menu = getPrimaryMenu(); | |
| 7919 | + if (menu) { | |
| 7920 | + foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { | |
| 7921 | + QString name = displayPlugin->getName(); | |
| 7922 | + QAction* action = menu->getActionForOption(name); | |
| 7923 | + // Menu might have been removed if the display plugin lost | |
| 7924 | + if (!action) { | |
| 7925 | + continue; | |
| 7926 | + } | |
| 7927 | + if (action->isChecked()) { | |
| 7928 | + newDisplayPlugin = displayPlugin; | |
| 7929 | + break; | |
| 7930 | + } | |
| 7559 | 7931 | } |
| 7560 | 7932 | } |
| 7561 | 7933 | |
| @@ -7563,8 +7935,21 @@ void Application::updateDisplayMode() { · 1 unmodified line | ||
| 7563 | 7935 | return; |
| 7564 | 7936 | } |
| 7565 | 7937 | |
| 7938 | + setDisplayPlugin(newDisplayPlugin); | |
| 7939 | +} | |
| 7940 | + | |
| 7941 | +void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { | |
| 7942 | + if (newDisplayPlugin == _displayPlugin) { | |
| 7943 | + return; | |
| 7944 | + } | |
| 7945 | + | |
| 7946 | + // FIXME don't have the application directly set the state of the UI, | |
| 7947 | + // instead emit a signal that the display plugin is changing and let | |
| 7948 | + // the desktop lock itself. Reduces coupling between the UI and display | |
| 7949 | + // plugins | |
| 7566 | 7950 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 7567 | 7951 | auto desktop = offscreenUi->getDesktop(); |
| 7952 | + auto menu = Menu::getInstance(); | |
| 7568 | 7953 | |
| 7569 | 7954 | // Make the switch atomic from the perspective of other threads |
| 7570 | 7955 | { |
| @@ -7572,8 +7957,8 @@ void Application::updateDisplayMode() { · 1 unmodified line | ||
| 7572 | 7957 | bool wasRepositionLocked = false; |
| 7573 | 7958 | if (desktop) { |
| 7574 | 7959 | // Tell the desktop to no reposition (which requires plugin info), until we have set the new plugin, below. |
| 7575 | − wasRepositionLocked = offscreenUi->getDesktop()->property("repositionLocked").toBool(); | |
| 7576 | − offscreenUi->getDesktop()->setProperty("repositionLocked", true); | |
| 7960 | + wasRepositionLocked = desktop->property("repositionLocked").toBool(); | |
| 7961 | + desktop->setProperty("repositionLocked", true); | |
| 7577 | 7962 | } |
| 7578 | 7963 | |
| 7579 | 7964 | if (_displayPlugin) { |
| @@ -7585,6 +7970,8 @@ void Application::updateDisplayMode() { · 5 unmodified lines | ||
| 7585 | 7970 | bool active = newDisplayPlugin->activate(); |
| 7586 | 7971 | |
| 7587 | 7972 | if (!active) { |
| 7973 | + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); | |
| 7974 | + | |
| 7588 | 7975 | // If the new plugin fails to activate, fallback to last display |
| 7589 | 7976 | qWarning() << "Failed to activate display: " << newDisplayPlugin->getName(); |
| 7590 | 7977 | newDisplayPlugin = oldDisplayPlugin; |
| @@ -7605,13 +7992,6 @@ void Application::updateDisplayMode() { · 14 unmodified lines | ||
| 7605 | 7992 | if (!active) { |
| 7606 | 7993 | qFatal("Failed to activate fallback plugin"); |
| 7607 | 7994 | } |
| 7608 | − | |
| 7609 | − // We've changed the selection - it should be reflected in the menu | |
| 7610 | − QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); | |
| 7611 | − if (!action) { | |
| 7612 | − qFatal("Failed to find activated plugin"); | |
| 7613 | − } | |
| 7614 | − action->setChecked(true); | |
| 7615 | 7995 | } |
| 7616 | 7996 | |
| 7617 | 7997 | offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); |
| @@ -7638,14 +8018,21 @@ void Application::updateDisplayMode() { · 20 unmodified lines | ||
| 7638 | 8018 | getMyAvatar()->reset(false); |
| 7639 | 8019 | |
| 7640 | 8020 | // switch to first person if entering hmd and setting is checked |
| 7641 | − if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { | |
| 7642 | − menu->setIsOptionChecked(MenuOption::FirstPerson, true); | |
| 7643 | − cameraMenuChanged(); | |
| 8021 | + if (menu) { | |
| 8022 | + QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); | |
| 8023 | + if (action) { | |
| 8024 | + action->setChecked(true); | |
| 8025 | + } | |
| 8026 | + | |
| 8027 | + if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { | |
| 8028 | + menu->setIsOptionChecked(MenuOption::FirstPerson, true); | |
| 8029 | + cameraMenuChanged(); | |
| 8030 | + } | |
| 8031 | + | |
| 8032 | + // Remove the mirror camera option from menu if in HMD mode | |
| 8033 | + auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror); | |
| 8034 | + mirrorAction->setVisible(!isHmd); | |
| 7644 | 8035 | } |
| 7645 | − | |
| 7646 | − // Remove the mirror camera option from menu if in HMD mode | |
| 7647 | − auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror); | |
| 7648 | − mirrorAction->setVisible(!isHmd); | |
| 7649 | 8036 | |
| 7650 | 8037 | Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); |
| 7651 | 8038 | } |
| @@ -7666,11 +8053,30 @@ void Application::switchDisplayMode() { · 14 unmodified lines | ||
| 7666 | 8053 | setActiveDisplayPlugin(DESKTOP_DISPLAY_PLUGIN_NAME); |
| 7667 | 8054 | startHMDStandBySession(); |
| 7668 | 8055 | } |
| 7669 | − emit activeDisplayPluginChanged(); | |
| 7670 | 8056 | } |
| 7671 | 8057 | _previousHMDWornStatus = currentHMDWornStatus; |
| 7672 | 8058 | } |
| 7673 | 8059 | |
| 8060 | +void Application::setShowBulletWireframe(bool value) { | |
| 8061 | + _physicsEngine->setShowBulletWireframe(value); | |
| 8062 | +} | |
| 8063 | + | |
| 8064 | +void Application::setShowBulletAABBs(bool value) { | |
| 8065 | + _physicsEngine->setShowBulletAABBs(value); | |
| 8066 | +} | |
| 8067 | + | |
| 8068 | +void Application::setShowBulletContactPoints(bool value) { | |
| 8069 | + _physicsEngine->setShowBulletContactPoints(value); | |
| 8070 | +} | |
| 8071 | + | |
| 8072 | +void Application::setShowBulletConstraints(bool value) { | |
| 8073 | + _physicsEngine->setShowBulletConstraints(value); | |
| 8074 | +} | |
| 8075 | + | |
| 8076 | +void Application::setShowBulletConstraintLimits(bool value) { | |
| 8077 | + _physicsEngine->setShowBulletConstraintLimits(value); | |
| 8078 | +} | |
| 8079 | + | |
| 7674 | 8080 | void Application::startHMDStandBySession() { |
| 7675 | 8081 | _autoSwitchDisplayModeSupportedHMDPlugin->startStandBySession(); |
| 7676 | 8082 | } |
| @@ -7721,15 +8127,18 @@ void Application::unresponsiveApplication() { · 44 unmodified lines | ||
| 7721 | 8127 | } |
| 7722 | 8128 | |
| 7723 | 8129 | void Application::setActiveDisplayPlugin(const QString& pluginName) { |
| 7724 | − auto menu = Menu::getInstance(); | |
| 7725 | − foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { | |
| 8130 | + DisplayPluginPointer newDisplayPlugin; | |
| 8131 | + for (DisplayPluginPointer displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) { | |
| 7726 | 8132 | QString name = displayPlugin->getName(); |
| 7727 | − QAction* action = menu->getActionForOption(name); | |
| 7728 | 8133 | if (pluginName == name) { |
| 7729 | − action->setChecked(true); | |
| 8134 | + newDisplayPlugin = displayPlugin; | |
| 8135 | + break; | |
| 7730 | 8136 | } |
| 7731 | 8137 | } |
| 7732 | − updateDisplayMode(); | |
| 8138 | + | |
| 8139 | + if (newDisplayPlugin) { | |
| 8140 | + setDisplayPlugin(newDisplayPlugin); | |
| 8141 | + } | |
| 7733 | 8142 | } |
| 7734 | 8143 | |
| 7735 | 8144 | void Application::handleLocalServerConnection() const { |
| @@ -7760,7 +8169,6 @@ void Application::readArgumentsFromLocalSocket() const { · 24 unmodified lines | ||
| 7760 | 8169 | } |
| 7761 | 8170 | |
| 7762 | 8171 | void Application::showDesktop() { |
| 7763 | − Menu::getInstance()->setIsOptionChecked(MenuOption::Overlays, true); | |
| 7764 | 8172 | } |
| 7765 | 8173 | |
| 7766 | 8174 | CompositorHelper& Application::getApplicationCompositor() const { |
| @@ -7849,4 +8257,22 @@ void Application::saveNextPhysicsStats(QString filename) { · 82 unmodified lines | ||
| 7849 | 8257 | _physicsEngine->saveNextPhysicsStats(filename); |
| 7850 | 8258 | } |
| 7851 | 8259 | |
| 8260 | +#if defined(Q_OS_ANDROID) | |
| 8261 | +void Application::enterBackground() { | |
| 8262 | + QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), | |
| 8263 | + "stop", Qt::BlockingQueuedConnection); | |
| 8264 | + if (getActiveDisplayPlugin()->isActive()) { | |
| 8265 | + getActiveDisplayPlugin()->deactivate(); | |
| 8266 | + } | |
| 8267 | +} | |
| 8268 | + | |
| 8269 | +void Application::enterForeground() { | |
| 8270 | + QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), | |
| 8271 | + "start", Qt::BlockingQueuedConnection); | |
| 8272 | + if (!getActiveDisplayPlugin() || getActiveDisplayPlugin()->isActive() || !getActiveDisplayPlugin()->activate()) { | |
| 8273 | + qWarning() << "Could not re-activate display plugin"; | |
| 8274 | + } | |
| 8275 | +} | |
| 8276 | +#endif | |
| 8277 | + | |
| 7852 | 8278 | #include "Application.moc" |