Showing the changes to interface/src/Application.cpp (1 of 179 files). Show all
Unified · Split
| @@ -42,16 +42,20 @@ · 41 unmodified lines | ||
| 42 | 42 | #include <QtMultimedia/QMediaPlayer> |
| 43 | 43 | |
| 44 | 44 | #include <QProcessEnvironment> |
| 45 | +#include <QTemporaryDir> | |
| 45 | 46 | |
| 46 | 47 | #include <gl/QOpenGLContextWrapper.h> |
| 47 | 48 | |
| 48 | 49 | #include <shared/GlobalAppProperties.h> |
| 50 | +#include <StatTracker.h> | |
| 51 | +#include <Trace.h> | |
| 49 | 52 | #include <ResourceScriptingInterface.h> |
| 50 | 53 | #include <AccountManager.h> |
| 51 | 54 | #include <AddressManager.h> |
| 52 | 55 | #include <AnimDebugDraw.h> |
| 53 | 56 | #include <BuildInfo.h> |
| 54 | 57 | #include <AssetClient.h> |
| 58 | +#include <AssetUpload.h> | |
| 55 | 59 | #include <AutoUpdater.h> |
| 56 | 60 | #include <AudioInjectorManager.h> |
| 57 | 61 | #include <CursorManager.h> |
| @@ -77,6 +81,7 @@ · 19 unmodified lines | ||
| 77 | 81 | #include <UserActivityLoggerScriptingInterface.h> |
| 78 | 82 | #include <LogHandler.h> |
| 79 | 83 | #include <MainWindow.h> |
| 84 | +#include <MappingRequest.h> | |
| 80 | 85 | #include <MessagesClient.h> |
| 81 | 86 | #include <ModelEntityItem.h> |
| 82 | 87 | #include <NetworkAccessManager.h> |
| @@ -156,6 +161,7 @@ · 73 unmodified lines | ||
| 156 | 161 | #include "ui/DialogsManager.h" |
| 157 | 162 | #include "ui/LoginDialog.h" |
| 158 | 163 | #include "ui/overlays/Cube3DOverlay.h" |
| 164 | +#include "ui/overlays/Web3DOverlay.h" | |
| 159 | 165 | #include "ui/Snapshot.h" |
| 160 | 166 | #include "ui/SnapshotAnimated.h" |
| 161 | 167 | #include "ui/StandAloneJSConsole.h" |
| @@ -178,6 +184,8 @@ extern "C" { · 16 unmodified lines | ||
| 178 | 184 | } |
| 179 | 185 | #endif |
| 180 | 186 | |
| 187 | +Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse") | |
| 188 | + | |
| 181 | 189 | using namespace std; |
| 182 | 190 | |
| 183 | 191 | static QTimer locationUpdateTimer; |
| @@ -424,6 +432,8 @@ bool setupEssentials(int& argc, char** argv) { · 240 unmodified lines | ||
| 424 | 432 | steamClient->init(); |
| 425 | 433 | } |
| 426 | 434 | |
| 435 | + DependencyManager::set<tracing::Tracer>(); | |
| 436 | + | |
| 427 | 437 | #if defined(Q_OS_WIN) |
| 428 | 438 | // Select appropriate audio DLL |
| 429 | 439 | QString audioDLLPath = QCoreApplication::applicationDirPath(); |
| @@ -446,6 +456,7 @@ bool setupEssentials(int& argc, char** argv) { · 16 unmodified lines | ||
| 446 | 456 | |
| 447 | 457 | // Set dependencies |
| 448 | 458 | DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp)); |
| 459 | + DependencyManager::set<StatTracker>(); | |
| 449 | 460 | DependencyManager::set<ScriptEngines>(); |
| 450 | 461 | DependencyManager::set<Preferences>(); |
| 451 | 462 | DependencyManager::set<recording::Deck>(); |
| @@ -559,6 +570,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 107 unmodified lines | ||
| 559 | 570 | |
| 560 | 571 | { |
| 561 | 572 | const QString TEST_SCRIPT = "--testScript"; |
| 573 | + const QString TRACE_FILE = "--traceFile"; | |
| 562 | 574 | const QStringList args = arguments(); |
| 563 | 575 | for (int i = 0; i < args.size() - 1; ++i) { |
| 564 | 576 | if (args.at(i) == TEST_SCRIPT) { |
| @@ -566,6 +578,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 1 unmodified line | ||
| 566 | 578 | if (QFileInfo(testScriptPath).exists()) { |
| 567 | 579 | setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); |
| 568 | 580 | } |
| 581 | + } else if (args.at(i) == TRACE_FILE) { | |
| 582 | + QString traceFilePath = args.at(i + 1); | |
| 583 | + setProperty(hifi::properties::TRACING, traceFilePath); | |
| 584 | + DependencyManager::get<tracing::Tracer>()->startTracing(); | |
| 569 | 585 | } |
| 570 | 586 | } |
| 571 | 587 | } |
| @@ -1180,11 +1196,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 608 unmodified lines | ||
| 1180 | 1196 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); |
| 1181 | 1197 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, |
| 1182 | 1198 | [this](const EntityItemID& entityItemID, const PointerEvent& event) { |
| 1199 | + setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | |
| 1183 | 1200 | setKeyboardFocusEntity(entityItemID); |
| 1184 | 1201 | }); |
| 1185 | 1202 | |
| 1186 | 1203 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) { |
| 1187 | − if (entityItemID == _keyboardFocusedItem.get()) { | |
| 1204 | + if (entityItemID == _keyboardFocusedEntity.get()) { | |
| 1188 | 1205 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); |
| 1189 | 1206 | } |
| 1190 | 1207 | }); |
| @@ -1194,7 +1211,26 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 3 unmodified lines | ||
| 1194 | 1211 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); |
| 1195 | 1212 | }); |
| 1196 | 1213 | |
| 1214 | + // Keyboard focus handling for Web overlays. | |
| 1215 | + auto overlays = &(qApp->getOverlays()); | |
| 1216 | + | |
| 1217 | + connect(overlays, &Overlays::mousePressOnOverlay, [=](unsigned int overlayID, const PointerEvent& event) { | |
| 1218 | + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); | |
| 1219 | + setKeyboardFocusOverlay(overlayID); | |
| 1220 | + }); | |
| 1221 | + | |
| 1222 | + connect(overlays, &Overlays::overlayDeleted, [=](unsigned int overlayID) { | |
| 1223 | + if (overlayID == _keyboardFocusedOverlay.get()) { | |
| 1224 | + setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | |
| 1225 | + } | |
| 1226 | + }); | |
| 1227 | + | |
| 1228 | + connect(overlays, &Overlays::mousePressOffOverlay, [=]() { | |
| 1229 | + setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | |
| 1230 | + }); | |
| 1231 | + | |
| 1197 | 1232 | connect(this, &Application::aboutToQuit, [=]() { |
| 1233 | + setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | |
| 1198 | 1234 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); |
| 1199 | 1235 | }); |
| 1200 | 1236 | |
| @@ -1381,16 +1417,31 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 180 unmodified lines | ||
| 1381 | 1417 | } |
| 1382 | 1418 | qCDebug(interfaceapp) << "Server content version: " << contentVersion; |
| 1383 | 1419 | |
| 1384 | − bool hasTutorialContent = contentVersion >= 1; | |
| 1420 | + static const int MIN_VIVE_CONTENT_VERSION = 1; | |
| 1421 | + static const int MIN_OCULUS_TOUCH_CONTENT_VERSION = 27; | |
| 1422 | + | |
| 1423 | + bool hasSufficientTutorialContent = false; | |
| 1424 | + bool hasHandControllers = false; | |
| 1425 | + | |
| 1426 | + // Only specific hand controllers are currently supported, so only send users to the tutorial | |
| 1427 | + // if they have one of those hand controllers. | |
| 1428 | + if (PluginUtils::isViveControllerAvailable()) { | |
| 1429 | + hasHandControllers = true; | |
| 1430 | + hasSufficientTutorialContent = contentVersion >= MIN_VIVE_CONTENT_VERSION; | |
| 1431 | + } else if (PluginUtils::isOculusTouchControllerAvailable()) { | |
| 1432 | + hasHandControllers = true; | |
| 1433 | + hasSufficientTutorialContent = contentVersion >= MIN_OCULUS_TOUCH_CONTENT_VERSION; | |
| 1434 | + } | |
| 1385 | 1435 | |
| 1386 | 1436 | Setting::Handle<bool> firstRun { Settings::firstRun, true }; |
| 1387 | − bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable("OpenVR (Vive)") && PluginUtils::isHandControllerAvailable(); | |
| 1437 | + | |
| 1438 | + bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable() && hasHandControllers; | |
| 1388 | 1439 | Setting::Handle<bool> tutorialComplete { "tutorialComplete", false }; |
| 1389 | 1440 | |
| 1390 | − bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get(); | |
| 1441 | + bool shouldGoToTutorial = hasHMDAndHandControllers && hasSufficientTutorialContent && !tutorialComplete.get(); | |
| 1391 | 1442 | |
| 1392 | 1443 | qCDebug(interfaceapp) << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName(); |
| 1393 | − qCDebug(interfaceapp) << "Has tutorial content: " << hasTutorialContent; | |
| 1444 | + qCDebug(interfaceapp) << "Has sufficient tutorial content (" << contentVersion << ") : " << hasSufficientTutorialContent; | |
| 1394 | 1445 | qCDebug(interfaceapp) << "Tutorial complete: " << tutorialComplete.get(); |
| 1395 | 1446 | qCDebug(interfaceapp) << "Should go to tutorial: " << shouldGoToTutorial; |
| 1396 | 1447 | |
| @@ -1451,6 +1502,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 54 unmodified lines | ||
| 1451 | 1502 | // After all of the constructor is completed, then set firstRun to false. |
| 1452 | 1503 | firstRun.set(false); |
| 1453 | 1504 | } |
| 1505 | + | |
| 1506 | + // Monitor model assets (e.g., from Clara.io) added to the world that may need resizing. | |
| 1507 | + static const int ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS = 1000; | |
| 1508 | + _addAssetToWorldResizeTimer.setInterval(ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS); | |
| 1509 | + connect(&_addAssetToWorldResizeTimer, &QTimer::timeout, this, &Application::addAssetToWorldCheckModelSize); | |
| 1510 | + | |
| 1511 | + // Auto-update and close adding asset to world info message box. | |
| 1512 | + static const int ADD_ASSET_TO_WORLD_INFO_TIMEOUT_MS = 5000; | |
| 1513 | + _addAssetToWorldInfoTimer.setInterval(ADD_ASSET_TO_WORLD_INFO_TIMEOUT_MS); | |
| 1514 | + _addAssetToWorldInfoTimer.setSingleShot(true); | |
| 1515 | + connect(&_addAssetToWorldInfoTimer, &QTimer::timeout, this, &Application::addAssetToWorldInfoTimeout); | |
| 1516 | + static const int ADD_ASSET_TO_WORLD_ERROR_TIMEOUT_MS = 8000; | |
| 1517 | + _addAssetToWorldErrorTimer.setInterval(ADD_ASSET_TO_WORLD_ERROR_TIMEOUT_MS); | |
| 1518 | + _addAssetToWorldErrorTimer.setSingleShot(true); | |
| 1519 | + connect(&_addAssetToWorldErrorTimer, &QTimer::timeout, this, &Application::addAssetToWorldErrorTimeout); | |
| 1520 | + | |
| 1521 | + connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose); | |
| 1522 | + connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose); | |
| 1454 | 1523 | } |
| 1455 | 1524 | |
| 1456 | 1525 | void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { |
| @@ -1579,6 +1648,13 @@ void Application::cleanupBeforeQuit() { · 122 unmodified lines | ||
| 1579 | 1648 | QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false"); |
| 1580 | 1649 | qCDebug(interfaceapp) << "QTWEBENGINE_REMOTE_DEBUGGING =" << webengineRemoteDebugging; |
| 1581 | 1650 | |
| 1651 | + if (tracing::enabled()) { | |
| 1652 | + auto tracer = DependencyManager::get<tracing::Tracer>(); | |
| 1653 | + tracer->stopTracing(); | |
| 1654 | + auto outputFile = property(hifi::properties::TRACING).toString(); | |
| 1655 | + tracer->serialize(outputFile); | |
| 1656 | + } | |
| 1657 | + | |
| 1582 | 1658 | // Stop third party processes so that they're not left running in the event of a subsequent shutdown crash. |
| 1583 | 1659 | #ifdef HAVE_DDE |
| 1584 | 1660 | DependencyManager::get<DdeFaceTracker>()->setEnabled(false); |
| @@ -1753,7 +1829,7 @@ void Application::initializeGL() { · 168 unmodified lines | ||
| 1753 | 1829 | // Set up the render engine |
| 1754 | 1830 | render::CullFunctor cullFunctor = LODManager::shouldRender; |
| 1755 | 1831 | _renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor); |
| 1756 | − static const QString RENDER_FORWARD = "RENDER_FORWARD"; | |
| 1832 | + static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; | |
| 1757 | 1833 | if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { |
| 1758 | 1834 | _renderEngine->addJob<RenderForwardTask>("RenderForwardTask", cullFunctor); |
| 1759 | 1835 | } else { |
| @@ -1838,9 +1914,9 @@ void Application::initializeUi() { · 78 unmodified lines | ||
| 1838 | 1914 | rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data()); |
| 1839 | 1915 | rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data()); |
| 1840 | 1916 | rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data()); |
| 1841 | − FileScriptingInterface* fileDownload = new FileScriptingInterface(engine); | |
| 1842 | − rootContext->setContextProperty("File", fileDownload); | |
| 1843 | − connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::showAssetServerWidget); | |
| 1917 | + _fileDownload = new FileScriptingInterface(engine); | |
| 1918 | + rootContext->setContextProperty("File", _fileDownload); | |
| 1919 | + connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip); | |
| 1844 | 1920 | rootContext->setContextProperty("MyAvatar", getMyAvatar().get()); |
| 1845 | 1921 | rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data()); |
| 1846 | 1922 | rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data()); |
| @@ -1856,6 +1932,7 @@ void Application::initializeUi() { · 9 unmodified lines | ||
| 1856 | 1932 | rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface()); |
| 1857 | 1933 | |
| 1858 | 1934 | rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data()); |
| 1935 | + rootContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data()); | |
| 1859 | 1936 | |
| 1860 | 1937 | rootContext->setContextProperty("Camera", &_myCamera); |
| 1861 | 1938 | |
| @@ -1945,7 +2022,7 @@ void Application::paintGL() { · 83 unmodified lines | ||
| 1945 | 2022 | _frameCount++; |
| 1946 | 2023 | |
| 1947 | 2024 | auto lastPaintBegin = usecTimestampNow(); |
| 1948 | − PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); | |
| 2025 | + PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); | |
| 1949 | 2026 | PerformanceTimer perfTimer("paintGL"); |
| 1950 | 2027 | |
| 1951 | 2028 | if (nullptr == _displayPlugin) { |
| @@ -2122,7 +2199,7 @@ void Application::paintGL() { · 170 unmodified lines | ||
| 2122 | 2199 | auto finalFramebuffer = framebufferCache->getFramebuffer(); |
| 2123 | 2200 | |
| 2124 | 2201 | { |
| 2125 | − PROFILE_RANGE(__FUNCTION__ "/mainRender"); | |
| 2202 | + PROFILE_RANGE(render, "/mainRender"); | |
| 2126 | 2203 | PerformanceTimer perfTimer("mainRender"); |
| 2127 | 2204 | renderArgs._boomOffset = boomOffset; |
| 2128 | 2205 | // Viewport is assigned to the size of the framebuffer |
| @@ -2177,7 +2254,7 @@ void Application::paintGL() { · 48 unmodified lines | ||
| 2177 | 2254 | frame->overlay = _applicationOverlay.getOverlayTexture(); |
| 2178 | 2255 | // deliver final scene rendering commands to the display plugin |
| 2179 | 2256 | { |
| 2180 | − PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); | |
| 2257 | + PROFILE_RANGE(render, "/pluginOutput"); | |
| 2181 | 2258 | PerformanceTimer perfTimer("pluginOutput"); |
| 2182 | 2259 | _frameCounter.increment(); |
| 2183 | 2260 | displayPlugin->submitFrame(frame); |
| @@ -2265,7 +2342,7 @@ void Application::resizeEvent(QResizeEvent* event) { · 81 unmodified lines | ||
| 2265 | 2342 | } |
| 2266 | 2343 | |
| 2267 | 2344 | void Application::resizeGL() { |
| 2268 | − PROFILE_RANGE(__FUNCTION__); | |
| 2345 | + PROFILE_RANGE(render, __FUNCTION__); | |
| 2269 | 2346 | if (nullptr == _displayPlugin) { |
| 2270 | 2347 | return; |
| 2271 | 2348 | } |
| @@ -2320,7 +2397,6 @@ bool Application::importSVOFromURL(const QString& urlString) { · 48 unmodified lines | ||
| 2320 | 2397 | } |
| 2321 | 2398 | |
| 2322 | 2399 | bool Application::event(QEvent* event) { |
| 2323 | − | |
| 2324 | 2400 | if (!Menu::getInstance()) { |
| 2325 | 2401 | return false; |
| 2326 | 2402 | } |
| @@ -2368,26 +2444,49 @@ bool Application::event(QEvent* event) { · 41 unmodified lines | ||
| 2368 | 2444 | } |
| 2369 | 2445 | |
| 2370 | 2446 | { |
| 2371 | − if (!_keyboardFocusedItem.get().isInvalidID()) { | |
| 2447 | + if (!_keyboardFocusedEntity.get().isInvalidID()) { | |
| 2372 | 2448 | switch (event->type()) { |
| 2373 | − case QEvent::KeyPress: | |
| 2374 | − case QEvent::KeyRelease: { | |
| 2375 | − auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 2376 | − auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get()); | |
| 2377 | − if (entity && entity->getEventHandler()) { | |
| 2378 | − event->setAccepted(false); | |
| 2379 | − QCoreApplication::sendEvent(entity->getEventHandler(), event); | |
| 2380 | − if (event->isAccepted()) { | |
| 2381 | − _lastAcceptedKeyPress = usecTimestampNow(); | |
| 2382 | − return true; | |
| 2449 | + case QEvent::KeyPress: | |
| 2450 | + case QEvent::KeyRelease: { | |
| 2451 | + //auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 2452 | + auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get()); | |
| 2453 | + if (entity && entity->getEventHandler()) { | |
| 2454 | + event->setAccepted(false); | |
| 2455 | + QCoreApplication::sendEvent(entity->getEventHandler(), event); | |
| 2456 | + if (event->isAccepted()) { | |
| 2457 | + _lastAcceptedKeyPress = usecTimestampNow(); | |
| 2458 | + return true; | |
| 2459 | + } | |
| 2383 | 2460 | } |
| 2461 | + break; | |
| 2384 | 2462 | } |
| 2385 | − break; | |
| 2386 | − } | |
| 2463 | + default: | |
| 2464 | + break; | |
| 2465 | + } | |
| 2466 | + } | |
| 2467 | + } | |
| 2387 | 2468 | |
| 2388 | − default: | |
| 2389 | − break; | |
| 2390 | − } | |
| 2469 | + { | |
| 2470 | + if (_keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) { | |
| 2471 | + switch (event->type()) { | |
| 2472 | + case QEvent::KeyPress: | |
| 2473 | + case QEvent::KeyRelease: { | |
| 2474 | + // Only Web overlays can have focus. | |
| 2475 | + auto overlay = | |
| 2476 | + std::dynamic_pointer_cast<Web3DOverlay>(getOverlays().getOverlay(_keyboardFocusedOverlay.get())); | |
| 2477 | + if (overlay && overlay->getEventHandler()) { | |
| 2478 | + event->setAccepted(false); | |
| 2479 | + QCoreApplication::sendEvent(overlay->getEventHandler(), event); | |
| 2480 | + if (event->isAccepted()) { | |
| 2481 | + _lastAcceptedKeyPress = usecTimestampNow(); | |
| 2482 | + return true; | |
| 2483 | + } | |
| 2484 | + } | |
| 2485 | + break; | |
| 2486 | + } | |
| 2487 | + default: | |
| 2488 | + break; | |
| 2489 | + } | |
| 2391 | 2490 | } |
| 2392 | 2491 | } |
| 2393 | 2492 | |
| @@ -2911,7 +3010,7 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) const { · 517 unmodified lines | ||
| 2911 | 3010 | } |
| 2912 | 3011 | |
| 2913 | 3012 | void Application::mouseMoveEvent(QMouseEvent* event) { |
| 2914 | − PROFILE_RANGE(__FUNCTION__); | |
| 3013 | + PROFILE_RANGE(app_input_mouse, __FUNCTION__); | |
| 2915 | 3014 | |
| 2916 | 3015 | if (_aboutToQuit) { |
| 2917 | 3016 | return; |
| @@ -2947,6 +3046,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 29 unmodified lines | ||
| 2947 | 3046 | |
| 2948 | 3047 | if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || |
| 2949 | 3048 | getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { |
| 3049 | + getOverlays().mouseMoveEvent(&mappedEvent); | |
| 2950 | 3050 | getEntities()->mouseMoveEvent(&mappedEvent); |
| 2951 | 3051 | } |
| 2952 | 3052 | _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts |
| @@ -2959,7 +3059,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 6 unmodified lines | ||
| 2959 | 3059 | if (_keyboardMouseDevice->isActive()) { |
| 2960 | 3060 | _keyboardMouseDevice->mouseMoveEvent(event); |
| 2961 | 3061 | } |
| 2962 | − | |
| 2963 | 3062 | } |
| 2964 | 3063 | |
| 2965 | 3064 | void Application::mousePressEvent(QMouseEvent* event) { |
| @@ -2981,6 +3080,7 @@ void Application::mousePressEvent(QMouseEvent* event) { · 15 unmodified lines | ||
| 2981 | 3080 | event->buttons(), event->modifiers()); |
| 2982 | 3081 | |
| 2983 | 3082 | if (!_aboutToQuit) { |
| 3083 | + getOverlays().mousePressEvent(&mappedEvent); | |
| 2984 | 3084 | getEntities()->mousePressEvent(&mappedEvent); |
| 2985 | 3085 | } |
| 2986 | 3086 | |
| @@ -2991,7 +3091,6 @@ void Application::mousePressEvent(QMouseEvent* event) { · 4 unmodified lines | ||
| 2991 | 3091 | return; |
| 2992 | 3092 | } |
| 2993 | 3093 | |
| 2994 | − | |
| 2995 | 3094 | if (hasFocus()) { |
| 2996 | 3095 | if (_keyboardMouseDevice->isActive()) { |
| 2997 | 3096 | _keyboardMouseDevice->mousePressEvent(event); |
| @@ -3002,7 +3101,6 @@ void Application::mousePressEvent(QMouseEvent* event) { · 4 unmodified lines | ||
| 3002 | 3101 | HFActionEvent actionEvent(HFActionEvent::startType(), |
| 3003 | 3102 | computePickRay(mappedEvent.x(), mappedEvent.y())); |
| 3004 | 3103 | sendEvent(this, &actionEvent); |
| 3005 | − | |
| 3006 | 3104 | } |
| 3007 | 3105 | } |
| 3008 | 3106 | } |
| @@ -3027,6 +3125,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { · 18 unmodified lines | ||
| 3027 | 3125 | event->buttons(), event->modifiers()); |
| 3028 | 3126 | |
| 3029 | 3127 | if (!_aboutToQuit) { |
| 3128 | + getOverlays().mouseReleaseEvent(&mappedEvent); | |
| 3030 | 3129 | getEntities()->mouseReleaseEvent(&mappedEvent); |
| 3031 | 3130 | } |
| 3032 | 3131 | |
| @@ -3205,6 +3304,7 @@ bool Application::shouldPaint(float nsecsElapsed) { · 172 unmodified lines | ||
| 3205 | 3304 | } |
| 3206 | 3305 | |
| 3207 | 3306 | void Application::idle(float nsecsElapsed) { |
| 3307 | + PerformanceTimer perfTimer("idle"); | |
| 3208 | 3308 | |
| 3209 | 3309 | // Update the deadlock watchdog |
| 3210 | 3310 | updateHeartbeat(); |
| @@ -3219,7 +3319,17 @@ void Application::idle(float nsecsElapsed) { · 8 unmodified lines | ||
| 3219 | 3319 | connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); |
| 3220 | 3320 | } |
| 3221 | 3321 | |
| 3222 | − PROFILE_RANGE(__FUNCTION__); | |
| 3322 | + PROFILE_COUNTER(app, "fps", { { "fps", _frameCounter.rate() } }); | |
| 3323 | + PROFILE_COUNTER(app, "downloads", { | |
| 3324 | + { "current", ResourceCache::getLoadingRequests().length() }, | |
| 3325 | + { "pending", ResourceCache::getPendingRequestCount() } | |
| 3326 | + }); | |
| 3327 | + PROFILE_COUNTER(app, "processing", { | |
| 3328 | + { "current", DependencyManager::get<StatTracker>()->getStat("Processing") }, | |
| 3329 | + { "pending", DependencyManager::get<StatTracker>()->getStat("PendingProcessing") } | |
| 3330 | + }); | |
| 3331 | + | |
| 3332 | + PROFILE_RANGE(app, __FUNCTION__); | |
| 3223 | 3333 | |
| 3224 | 3334 | if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { |
| 3225 | 3335 | steamClient->runCallbacks(); |
| @@ -3241,8 +3351,6 @@ void Application::idle(float nsecsElapsed) { · 15 unmodified lines | ||
| 3241 | 3351 | |
| 3242 | 3352 | _simCounter.increment(); |
| 3243 | 3353 | |
| 3244 | − PerformanceTimer perfTimer("idle"); | |
| 3245 | − | |
| 3246 | 3354 | // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing |
| 3247 | 3355 | // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing |
| 3248 | 3356 | // details normally. |
| @@ -3257,20 +3365,30 @@ void Application::idle(float nsecsElapsed) { · 8 unmodified lines | ||
| 3257 | 3365 | } |
| 3258 | 3366 | |
| 3259 | 3367 | |
| 3260 | − // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds | |
| 3368 | + // Update focus highlight for entity or overlay. | |
| 3261 | 3369 | { |
| 3262 | − if (!_keyboardFocusedItem.get().isInvalidID()) { | |
| 3370 | + if (!_keyboardFocusedEntity.get().isInvalidID() || _keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) { | |
| 3263 | 3371 | const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus |
| 3264 | 3372 | quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; |
| 3265 | 3373 | if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { |
| 3266 | 3374 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); |
| 3375 | + setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | |
| 3267 | 3376 | } else { |
| 3268 | 3377 | // update position of highlight overlay |
| 3269 | − auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 3270 | − auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get()); | |
| 3271 | − if (entity && _keyboardFocusHighlight) { | |
| 3272 | − _keyboardFocusHighlight->setRotation(entity->getRotation()); | |
| 3273 | − _keyboardFocusHighlight->setPosition(entity->getPosition()); | |
| 3378 | + if (!_keyboardFocusedEntity.get().isInvalidID()) { | |
| 3379 | + auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get()); | |
| 3380 | + if (entity && _keyboardFocusHighlight) { | |
| 3381 | + _keyboardFocusHighlight->setRotation(entity->getRotation()); | |
| 3382 | + _keyboardFocusHighlight->setPosition(entity->getPosition()); | |
| 3383 | + } | |
| 3384 | + } else { | |
| 3385 | + // Only Web overlays can have focus. | |
| 3386 | + auto overlay = | |
| 3387 | + std::dynamic_pointer_cast<Web3DOverlay>(getOverlays().getOverlay(_keyboardFocusedOverlay.get())); | |
| 3388 | + if (overlay && _keyboardFocusHighlight) { | |
| 3389 | + _keyboardFocusHighlight->setRotation(overlay->getRotation()); | |
| 3390 | + _keyboardFocusHighlight->setPosition(overlay->getPosition()); | |
| 3391 | + } | |
| 3274 | 3392 | } |
| 3275 | 3393 | } |
| 3276 | 3394 | } |
| @@ -3858,8 +3976,31 @@ void Application::rotationModeChanged() const { · 581 unmodified lines | ||
| 3858 | 3976 | } |
| 3859 | 3977 | } |
| 3860 | 3978 | |
| 3979 | +void Application::setKeyboardFocusHighlight(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions) { | |
| 3980 | + // Create focus | |
| 3981 | + if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { | |
| 3982 | + _keyboardFocusHighlight = std::make_shared<Cube3DOverlay>(); | |
| 3983 | + _keyboardFocusHighlight->setAlpha(1.0f); | |
| 3984 | + _keyboardFocusHighlight->setBorderSize(1.0f); | |
| 3985 | + _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); | |
| 3986 | + _keyboardFocusHighlight->setIsSolid(false); | |
| 3987 | + _keyboardFocusHighlight->setPulseMin(0.5); | |
| 3988 | + _keyboardFocusHighlight->setPulseMax(1.0); | |
| 3989 | + _keyboardFocusHighlight->setColorPulse(1.0); | |
| 3990 | + _keyboardFocusHighlight->setIgnoreRayIntersection(true); | |
| 3991 | + _keyboardFocusHighlight->setDrawInFront(false); | |
| 3992 | + _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); | |
| 3993 | + } | |
| 3994 | + | |
| 3995 | + // Position focus | |
| 3996 | + _keyboardFocusHighlight->setRotation(rotation); | |
| 3997 | + _keyboardFocusHighlight->setPosition(position); | |
| 3998 | + _keyboardFocusHighlight->setDimensions(dimensions); | |
| 3999 | + _keyboardFocusHighlight->setVisible(true); | |
| 4000 | +} | |
| 4001 | + | |
| 3861 | 4002 | QUuid Application::getKeyboardFocusEntity() const { |
| 3862 | − return _keyboardFocusedItem.get(); | |
| 4003 | + return _keyboardFocusedEntity.get(); | |
| 3863 | 4004 | } |
| 3864 | 4005 | |
| 3865 | 4006 | void Application::setKeyboardFocusEntity(QUuid id) { |
| @@ -3867,21 +4008,21 @@ void Application::setKeyboardFocusEntity(QUuid id) { · 1 unmodified line | ||
| 3867 | 4008 | setKeyboardFocusEntity(entityItemID); |
| 3868 | 4009 | } |
| 3869 | 4010 | |
| 4011 | +static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f; | |
| 4012 | + | |
| 3870 | 4013 | void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { |
| 3871 | − auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 3872 | − if (_keyboardFocusedItem.get() != entityItemID) { | |
| 3873 | − // reset focused entity | |
| 3874 | − _keyboardFocusedItem.set(UNKNOWN_ENTITY_ID); | |
| 3875 | − if (_keyboardFocusHighlight) { | |
| 4014 | + if (_keyboardFocusedEntity.get() != entityItemID) { | |
| 4015 | + _keyboardFocusedEntity.set(entityItemID); | |
| 4016 | + | |
| 4017 | + if (_keyboardFocusHighlight && _keyboardFocusedOverlay.get() == UNKNOWN_OVERLAY_ID) { | |
| 3876 | 4018 | _keyboardFocusHighlight->setVisible(false); |
| 3877 | 4019 | } |
| 3878 | 4020 | |
| 3879 | − // if invalid, return without expensive (locking) operations | |
| 3880 | 4021 | if (entityItemID == UNKNOWN_ENTITY_ID) { |
| 3881 | 4022 | return; |
| 3882 | 4023 | } |
| 3883 | 4024 | |
| 3884 | − // if valid, query properties | |
| 4025 | + auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 3885 | 4026 | auto properties = entityScriptingInterface->getEntityProperties(entityItemID); |
| 3886 | 4027 | if (!properties.getLocked() && properties.getVisible()) { |
| 3887 | 4028 | auto entity = getEntities()->getTree()->findEntityByID(entityItemID); |
| @@ -3890,30 +4031,45 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { · 2 unmodified lines | ||
| 3890 | 4031 | if (_keyboardMouseDevice->isActive()) { |
| 3891 | 4032 | _keyboardMouseDevice->pluginFocusOutEvent(); |
| 3892 | 4033 | } |
| 3893 | − _keyboardFocusedItem.set(entityItemID); | |
| 3894 | 4034 | _lastAcceptedKeyPress = usecTimestampNow(); |
| 3895 | 4035 | |
| 3896 | − // create a focus | |
| 3897 | − if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { | |
| 3898 | − _keyboardFocusHighlight = std::make_shared<Cube3DOverlay>(); | |
| 3899 | − _keyboardFocusHighlight->setAlpha(1.0f); | |
| 3900 | − _keyboardFocusHighlight->setBorderSize(1.0f); | |
| 3901 | − _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); | |
| 3902 | − _keyboardFocusHighlight->setIsSolid(false); | |
| 3903 | − _keyboardFocusHighlight->setPulseMin(0.5); | |
| 3904 | − _keyboardFocusHighlight->setPulseMax(1.0); | |
| 3905 | − _keyboardFocusHighlight->setColorPulse(1.0); | |
| 3906 | − _keyboardFocusHighlight->setIgnoreRayIntersection(true); | |
| 3907 | − _keyboardFocusHighlight->setDrawInFront(false); | |
| 3908 | − _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); | |
| 3909 | − } | |
| 4036 | + setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(), | |
| 4037 | + entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR); | |
| 4038 | + } | |
| 4039 | + } | |
| 4040 | + } | |
| 4041 | +} | |
| 4042 | + | |
| 4043 | +unsigned int Application::getKeyboardFocusOverlay() { | |
| 4044 | + return _keyboardFocusedOverlay.get(); | |
| 4045 | +} | |
| 4046 | + | |
| 4047 | +void Application::setKeyboardFocusOverlay(unsigned int overlayID) { | |
| 4048 | + if (overlayID != _keyboardFocusedOverlay.get()) { | |
| 4049 | + _keyboardFocusedOverlay.set(overlayID); | |
| 4050 | + | |
| 4051 | + if (_keyboardFocusHighlight && _keyboardFocusedEntity.get() == UNKNOWN_ENTITY_ID) { | |
| 4052 | + _keyboardFocusHighlight->setVisible(false); | |
| 4053 | + } | |
| 4054 | + | |
| 4055 | + if (overlayID == UNKNOWN_OVERLAY_ID) { | |
| 4056 | + return; | |
| 4057 | + } | |
| 4058 | + | |
| 4059 | + auto overlayType = getOverlays().getOverlayType(overlayID); | |
| 4060 | + auto isVisible = getOverlays().getProperty(overlayID, "visible").value.toBool(); | |
| 4061 | + if (overlayType == Web3DOverlay::TYPE && isVisible) { | |
| 4062 | + auto overlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlays().getOverlay(overlayID)); | |
| 4063 | + overlay->setProxyWindow(_window->windowHandle()); | |
| 3910 | 4064 | |
| 3911 | − // position the focus | |
| 3912 | − _keyboardFocusHighlight->setRotation(entity->getRotation()); | |
| 3913 | − _keyboardFocusHighlight->setPosition(entity->getPosition()); | |
| 3914 | − _keyboardFocusHighlight->setDimensions(entity->getDimensions() * 1.05f); | |
| 3915 | − _keyboardFocusHighlight->setVisible(true); | |
| 4065 | + if (_keyboardMouseDevice->isActive()) { | |
| 4066 | + _keyboardMouseDevice->pluginFocusOutEvent(); | |
| 3916 | 4067 | } |
| 4068 | + _lastAcceptedKeyPress = usecTimestampNow(); | |
| 4069 | + | |
| 4070 | + auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR; | |
| 4071 | + const float OVERLAY_DEPTH = 0.0105f; | |
| 4072 | + setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH)); | |
| 3917 | 4073 | } |
| 3918 | 4074 | } |
| 3919 | 4075 | } |
| @@ -3936,9 +4092,11 @@ void Application::updateDialogs(float deltaTime) const { · 16 unmodified lines | ||
| 3936 | 4092 | } |
| 3937 | 4093 | } |
| 3938 | 4094 | |
| 4095 | +static bool domainLoadingInProgress = false; | |
| 4096 | + | |
| 3939 | 4097 | void Application::update(float deltaTime) { |
| 3940 | 4098 | |
| 3941 | − PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1); | |
| 4099 | + PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1); | |
| 3942 | 4100 | |
| 3943 | 4101 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); |
| 3944 | 4102 | PerformanceWarning warn(showWarnings, "Application::update()"); |
| @@ -3946,6 +4104,11 @@ void Application::update(float deltaTime) { · 1 unmodified line | ||
| 3946 | 4104 | updateLOD(); |
| 3947 | 4105 | |
| 3948 | 4106 | if (!_physicsEnabled) { |
| 4107 | + if (!domainLoadingInProgress) { | |
| 4108 | + PROFILE_ASYNC_BEGIN(app, "Scene Loading", ""); | |
| 4109 | + domainLoadingInProgress = true; | |
| 4110 | + } | |
| 4111 | + | |
| 3949 | 4112 | // we haven't yet enabled physics. we wait until we think we have all the collision information |
| 3950 | 4113 | // for nearby entities before starting bullet up. |
| 3951 | 4114 | quint64 now = usecTimestampNow(); |
| @@ -3975,6 +4138,9 @@ void Application::update(float deltaTime) { · 23 unmodified lines | ||
| 3975 | 4138 | } |
| 3976 | 4139 | } |
| 3977 | 4140 | } |
| 4141 | + } else if (domainLoadingInProgress) { | |
| 4142 | + domainLoadingInProgress = false; | |
| 4143 | + PROFILE_ASYNC_END(app, "Scene Loading", ""); | |
| 3978 | 4144 | } |
| 3979 | 4145 | |
| 3980 | 4146 | { |
| @@ -4068,12 +4234,12 @@ void Application::update(float deltaTime) { · 87 unmodified lines | ||
| 4068 | 4234 | QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>(); |
| 4069 | 4235 | |
| 4070 | 4236 | if (_physicsEnabled) { |
| 4071 | − PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4237 | + PROFILE_RANGE_EX(simulation_physics, "Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4072 | 4238 | |
| 4073 | 4239 | PerformanceTimer perfTimer("physics"); |
| 4074 | 4240 | |
| 4075 | 4241 | { |
| 4076 | − PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4242 | + PROFILE_RANGE_EX(simulation_physics, "UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4077 | 4243 | |
| 4078 | 4244 | PerformanceTimer perfTimer("updateStates)"); |
| 4079 | 4245 | static VectorOfMotionStates motionStates; |
| @@ -4107,14 +4273,14 @@ void Application::update(float deltaTime) { · 27 unmodified lines | ||
| 4107 | 4273 | }); |
| 4108 | 4274 | } |
| 4109 | 4275 | { |
| 4110 | − PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4276 | + PROFILE_RANGE_EX(simulation_physics, "StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4111 | 4277 | PerformanceTimer perfTimer("stepSimulation"); |
| 4112 | 4278 | getEntities()->getTree()->withWriteLock([&] { |
| 4113 | 4279 | _physicsEngine->stepSimulation(); |
| 4114 | 4280 | }); |
| 4115 | 4281 | } |
| 4116 | 4282 | { |
| 4117 | − PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4283 | + PROFILE_RANGE_EX(simulation_physics, "HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4118 | 4284 | PerformanceTimer perfTimer("harvestChanges"); |
| 4119 | 4285 | if (_physicsEngine->hasOutgoingChanges()) { |
| 4120 | 4286 | getEntities()->getTree()->withWriteLock([&] { |
| @@ -4156,20 +4322,20 @@ void Application::update(float deltaTime) { · 35 unmodified lines | ||
| 4156 | 4322 | _avatarSimCounter.increment(); |
| 4157 | 4323 | |
| 4158 | 4324 | { |
| 4159 | − PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4325 | + PROFILE_RANGE_EX(simulation, "OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4160 | 4326 | avatarManager->updateOtherAvatars(deltaTime); |
| 4161 | 4327 | } |
| 4162 | 4328 | |
| 4163 | 4329 | qApp->updateMyAvatarLookAtPosition(); |
| 4164 | 4330 | |
| 4165 | 4331 | { |
| 4166 | − PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4332 | + PROFILE_RANGE_EX(simulation, "MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4167 | 4333 | avatarManager->updateMyAvatar(deltaTime); |
| 4168 | 4334 | } |
| 4169 | 4335 | } |
| 4170 | 4336 | |
| 4171 | 4337 | { |
| 4172 | − PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4338 | + PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4173 | 4339 | PerformanceTimer perfTimer("overlays"); |
| 4174 | 4340 | _overlays.update(deltaTime); |
| 4175 | 4341 | } |
| @@ -4189,7 +4355,7 @@ void Application::update(float deltaTime) { · 13 unmodified lines | ||
| 4189 | 4355 | |
| 4190 | 4356 | // Update my voxel servers with my current voxel query... |
| 4191 | 4357 | { |
| 4192 | − PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4358 | + PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 4193 | 4359 | QMutexLocker viewLocker(&_viewMutex); |
| 4194 | 4360 | PerformanceTimer perfTimer("queryOctree"); |
| 4195 | 4361 | quint64 sinceLastQuery = now - _lastQueriedTime; |
| @@ -4229,7 +4395,7 @@ void Application::update(float deltaTime) { · 33 unmodified lines | ||
| 4229 | 4395 | avatarManager->postUpdate(deltaTime); |
| 4230 | 4396 | |
| 4231 | 4397 | { |
| 4232 | − PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0); | |
| 4398 | + PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0); | |
| 4233 | 4399 | |
| 4234 | 4400 | std::unique_lock<std::mutex> guard(_postUpdateLambdasLock); |
| 4235 | 4401 | for (auto& iter : _postUpdateLambdas) { |
| @@ -4506,8 +4672,6 @@ QRect Application::getDesirableApplicationGeometry() const { · 270 unmodified lines | ||
| 4506 | 4672 | // or the "myCamera". |
| 4507 | 4673 | // |
| 4508 | 4674 | void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { |
| 4509 | − PerformanceTimer perfTimer("loadViewFrustum"); | |
| 4510 | − PROFILE_RANGE(__FUNCTION__); | |
| 4511 | 4675 | // We will use these below, from either the camera or head vectors calculated above |
| 4512 | 4676 | viewFrustum.setProjection(camera.getProjection()); |
| 4513 | 4677 | |
| @@ -4683,7 +4847,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 169 unmodified lines | ||
| 4683 | 4847 | myAvatar->preDisplaySide(renderArgs); |
| 4684 | 4848 | |
| 4685 | 4849 | activeRenderingThread = QThread::currentThread(); |
| 4686 | − PROFILE_RANGE(__FUNCTION__); | |
| 4850 | + PROFILE_RANGE(render, __FUNCTION__); | |
| 4687 | 4851 | PerformanceTimer perfTimer("display"); |
| 4688 | 4852 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); |
| 4689 | 4853 | |
| @@ -5468,6 +5632,464 @@ void Application::showAssetServerWidget(QString filePath) { · 778 unmodified lines | ||
| 5468 | 5632 | startUpload(nullptr, nullptr); |
| 5469 | 5633 | } |
| 5470 | 5634 | |
| 5635 | +void Application::addAssetToWorldFromURL(QString url) { | |
| 5636 | + qInfo(interfaceapp) << "Download asset and add to world from" << url; | |
| 5637 | + | |
| 5638 | + QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL. | |
| 5639 | + | |
| 5640 | + if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) { | |
| 5641 | + QString errorInfo = "You do not have permissions to write to the Asset Server."; | |
| 5642 | + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; | |
| 5643 | + addAssetToWorldError(filename, errorInfo); | |
| 5644 | + return; | |
| 5645 | + } | |
| 5646 | + | |
| 5647 | + addAssetToWorldInfo(filename, "Downloading asset file " + filename + "."); | |
| 5648 | + | |
| 5649 | + auto request = ResourceManager::createResourceRequest(nullptr, QUrl(url)); | |
| 5650 | + connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished); | |
| 5651 | + request->send(); | |
| 5652 | +} | |
| 5653 | + | |
| 5654 | +void Application::addAssetToWorldFromURLRequestFinished() { | |
| 5655 | + auto request = qobject_cast<ResourceRequest*>(sender()); | |
| 5656 | + auto url = request->getUrl().toString(); | |
| 5657 | + auto result = request->getResult(); | |
| 5658 | + | |
| 5659 | + QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter. | |
| 5660 | + | |
| 5661 | + if (result == ResourceRequest::Success) { | |
| 5662 | + qInfo(interfaceapp) << "Downloaded asset from" << url; | |
| 5663 | + QTemporaryDir temporaryDir; | |
| 5664 | + temporaryDir.setAutoRemove(false); | |
| 5665 | + if (temporaryDir.isValid()) { | |
| 5666 | + QString temporaryDirPath = temporaryDir.path(); | |
| 5667 | + QString downloadPath = temporaryDirPath + "/" + filename; | |
| 5668 | + qInfo(interfaceapp) << "Download path:" << downloadPath; | |
| 5669 | + | |
| 5670 | + QFile tempFile(downloadPath); | |
| 5671 | + if (tempFile.open(QIODevice::WriteOnly)) { | |
| 5672 | + tempFile.write(request->getData()); | |
| 5673 | + addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key. | |
| 5674 | + qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true); | |
| 5675 | + } else { | |
| 5676 | + QString errorInfo = "Couldn't open temporary file for download"; | |
| 5677 | + qWarning(interfaceapp) << errorInfo; | |
| 5678 | + addAssetToWorldError(filename, errorInfo); | |
| 5679 | + } | |
| 5680 | + } else { | |
| 5681 | + QString errorInfo = "Couldn't create temporary directory for download"; | |
| 5682 | + qWarning(interfaceapp) << errorInfo; | |
| 5683 | + addAssetToWorldError(filename, errorInfo); | |
| 5684 | + } | |
| 5685 | + } else { | |
| 5686 | + qWarning(interfaceapp) << "Error downloading" << url << ":" << request->getResultString(); | |
| 5687 | + addAssetToWorldError(filename, "Error downloading " + filename + " : " + request->getResultString()); | |
| 5688 | + } | |
| 5689 | + | |
| 5690 | + request->deleteLater(); | |
| 5691 | +} | |
| 5692 | + | |
| 5693 | + | |
| 5694 | +QString filenameFromPath(QString filePath) { | |
| 5695 | + return filePath.right(filePath.length() - filePath.lastIndexOf("/") - 1); | |
| 5696 | +} | |
| 5697 | + | |
| 5698 | +void Application::addAssetToWorldUnzipFailure(QString filePath) { | |
| 5699 | + QString filename = filenameFromPath(QUrl(filePath).toLocalFile()); | |
| 5700 | + qWarning(interfaceapp) << "Couldn't unzip file" << filePath; | |
| 5701 | + addAssetToWorldError(filename, "Couldn't unzip file " + filename + "."); | |
| 5702 | +} | |
| 5703 | + | |
| 5704 | +void Application::addAssetToWorld(QString filePath) { | |
| 5705 | + // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). | |
| 5706 | + | |
| 5707 | + QString path = QUrl(filePath).toLocalFile(); | |
| 5708 | + QString filename = filenameFromPath(path); | |
| 5709 | + QString mapping = "/" + filename; | |
| 5710 | + | |
| 5711 | + // Test repeated because possibly different code paths. | |
| 5712 | + if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) { | |
| 5713 | + QString errorInfo = "You do not have permissions to write to the Asset Server."; | |
| 5714 | + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; | |
| 5715 | + addAssetToWorldError(filename, errorInfo); | |
| 5716 | + return; | |
| 5717 | + } | |
| 5718 | + | |
| 5719 | + addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server."); | |
| 5720 | + | |
| 5721 | + addAssetToWorldWithNewMapping(path, mapping, 0); | |
| 5722 | +} | |
| 5723 | + | |
| 5724 | +void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) { | |
| 5725 | + auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(mapping); | |
| 5726 | + | |
| 5727 | + QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { | |
| 5728 | + const int MAX_COPY_COUNT = 100; // Limit number of duplicate assets; recursion guard. | |
| 5729 | + auto result = request->getError(); | |
| 5730 | + if (result == GetMappingRequest::NotFound) { | |
| 5731 | + addAssetToWorldUpload(filePath, mapping); | |
| 5732 | + } else if (result != GetMappingRequest::NoError) { | |
| 5733 | + QString errorInfo = "Could not map asset name: " | |
| 5734 | + + mapping.left(mapping.length() - QString::number(copy).length() - 1); | |
| 5735 | + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; | |
| 5736 | + addAssetToWorldError(filenameFromPath(filePath), errorInfo); | |
| 5737 | + } else if (copy < MAX_COPY_COUNT - 1) { | |
| 5738 | + if (copy > 0) { | |
| 5739 | + mapping = mapping.remove(mapping.lastIndexOf("-"), QString::number(copy).length() + 1); | |
| 5740 | + } | |
| 5741 | + copy++; | |
| 5742 | + mapping = mapping.insert(mapping.lastIndexOf("."), "-" + QString::number(copy)); | |
| 5743 | + addAssetToWorldWithNewMapping(filePath, mapping, copy); | |
| 5744 | + } else { | |
| 5745 | + QString errorInfo = "Too many copies of asset name: " | |
| 5746 | + + mapping.left(mapping.length() - QString::number(copy).length() - 1); | |
| 5747 | + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; | |
| 5748 | + addAssetToWorldError(filenameFromPath(filePath), errorInfo); | |
| 5749 | + } | |
| 5750 | + request->deleteLater(); | |
| 5751 | + }); | |
| 5752 | + | |
| 5753 | + request->start(); | |
| 5754 | +} | |
| 5755 | + | |
| 5756 | +void Application::addAssetToWorldUpload(QString filePath, QString mapping) { | |
| 5757 | + qInfo(interfaceapp) << "Uploading" << filePath << "to Asset Server as" << mapping; | |
| 5758 | + auto upload = DependencyManager::get<AssetClient>()->createUpload(filePath); | |
| 5759 | + QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { | |
| 5760 | + if (upload->getError() != AssetUpload::NoError) { | |
| 5761 | + QString errorInfo = "Could not upload asset to the Asset Server."; | |
| 5762 | + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; | |
| 5763 | + addAssetToWorldError(filenameFromPath(filePath), errorInfo); | |
| 5764 | + } else { | |
| 5765 | + addAssetToWorldSetMapping(filePath, mapping, hash); | |
| 5766 | + } | |
| 5767 | + | |
| 5768 | + // Remove temporary directory created by Clara.io market place download. | |
| 5769 | + int index = filePath.lastIndexOf("/model_repo/"); | |
| 5770 | + if (index > 0) { | |
| 5771 | + QString tempDir = filePath.left(index); | |
| 5772 | + qCDebug(interfaceapp) << "Removing temporary directory at: " + tempDir; | |
| 5773 | + QDir(tempDir).removeRecursively(); | |
| 5774 | + } | |
| 5775 | + | |
| 5776 | + upload->deleteLater(); | |
| 5777 | + }); | |
| 5778 | + | |
| 5779 | + upload->start(); | |
| 5780 | +} | |
| 5781 | + | |
| 5782 | +void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash) { | |
| 5783 | + auto request = DependencyManager::get<AssetClient>()->createSetMappingRequest(mapping, hash); | |
| 5784 | + connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable { | |
| 5785 | + if (request->getError() != SetMappingRequest::NoError) { | |
| 5786 | + QString errorInfo = "Could not set asset mapping."; | |
| 5787 | + qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; | |
| 5788 | + addAssetToWorldError(filenameFromPath(filePath), errorInfo); | |
| 5789 | + } else { | |
| 5790 | + addAssetToWorldAddEntity(filePath, mapping); | |
| 5791 | + } | |
| 5792 | + request->deleteLater(); | |
| 5793 | + }); | |
| 5794 | + | |
| 5795 | + request->start(); | |
| 5796 | +} | |
| 5797 | + | |
| 5798 | +void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) { | |
| 5799 | + EntityItemProperties properties; | |
| 5800 | + properties.setType(EntityTypes::Model); | |
| 5801 | + properties.setName(mapping.right(mapping.length() - 1)); | |
| 5802 | + properties.setModelURL("atp:" + mapping); | |
| 5803 | + properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); | |
| 5804 | + properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar. | |
| 5805 | + properties.setVisible(false); // Temporarily set so that don't see at large unresized dimensions. | |
| 5806 | + properties.setPosition(getMyAvatar()->getPosition() + getMyAvatar()->getOrientation() * glm::vec3(0.0f, 0.0f, -2.0f)); | |
| 5807 | + properties.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); | |
| 5808 | + auto entityID = DependencyManager::get<EntityScriptingInterface>()->addEntity(properties); | |
| 5809 | + | |
| 5810 | + // Note: Model dimensions are not available here; model is scaled per FBX mesh in RenderableModelEntityItem::update() later | |
| 5811 | + // on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted. | |
| 5812 | + | |
| 5813 | + if (entityID == QUuid()) { | |
| 5814 | + QString errorInfo = "Could not add asset " + mapping + " to world."; | |
| 5815 | + qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo; | |
| 5816 | + addAssetToWorldError(filenameFromPath(filePath), errorInfo); | |
| 5817 | + } else { | |
| 5818 | + // Monitor when asset is rendered in world so that can resize if necessary. | |
| 5819 | + _addAssetToWorldResizeList.insert(entityID, 0); // List value is count of checks performed. | |
| 5820 | + if (!_addAssetToWorldResizeTimer.isActive()) { | |
| 5821 | + _addAssetToWorldResizeTimer.start(); | |
| 5822 | + } | |
| 5823 | + | |
| 5824 | + // Close progress message box. | |
| 5825 | + addAssetToWorldInfoDone(filenameFromPath(filePath)); | |
| 5826 | + } | |
| 5827 | +} | |
| 5828 | + | |
| 5829 | +void Application::addAssetToWorldCheckModelSize() { | |
| 5830 | + if (_addAssetToWorldResizeList.size() == 0) { | |
| 5831 | + return; | |
| 5832 | + } | |
| 5833 | + | |
| 5834 | + auto item = _addAssetToWorldResizeList.begin(); | |
| 5835 | + while (item != _addAssetToWorldResizeList.end()) { | |
| 5836 | + auto entityID = item.key(); | |
| 5837 | + | |
| 5838 | + EntityPropertyFlags propertyFlags; | |
| 5839 | + propertyFlags += PROP_NAME; | |
| 5840 | + propertyFlags += PROP_DIMENSIONS; | |
| 5841 | + auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | |
| 5842 | + auto properties = entityScriptingInterface->getEntityProperties(entityID, propertyFlags); | |
| 5843 | + auto name = properties.getName(); | |
| 5844 | + auto dimensions = properties.getDimensions(); | |
| 5845 | + | |
| 5846 | + const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; | |
| 5847 | + bool doResize = false; | |
| 5848 | + | |
| 5849 | + const glm::vec3 DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f); | |
| 5850 | + if (dimensions != DEFAULT_DIMENSIONS) { | |
| 5851 | + | |
| 5852 | + // Scale model so that its maximum is exactly specific size. | |
| 5853 | + const float MAXIMUM_DIMENSION = 1.0f; | |
| 5854 | + auto previousDimensions = dimensions; | |
| 5855 | + auto scale = std::min(MAXIMUM_DIMENSION / dimensions.x, std::min(MAXIMUM_DIMENSION / dimensions.y, | |
| 5856 | + MAXIMUM_DIMENSION / dimensions.z)); | |
| 5857 | + dimensions *= scale; | |
| 5858 | + qInfo(interfaceapp) << "Asset" << name << "auto-resized from" << previousDimensions << " to " << dimensions; | |
| 5859 | + doResize = true; | |
| 5860 | + | |
| 5861 | + item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next. | |
| 5862 | + } else { | |
| 5863 | + // Increment count of checks done. | |
| 5864 | + _addAssetToWorldResizeList[entityID]++; | |
| 5865 | + | |
| 5866 | + const int CHECK_MODEL_SIZE_MAX_CHECKS = 300; | |
| 5867 | + if (_addAssetToWorldResizeList[entityID] > CHECK_MODEL_SIZE_MAX_CHECKS) { | |
| 5868 | + // Have done enough checks; model was either the default size or something's gone wrong. | |
| 5869 | + | |
| 5870 | + // Rescale all dimensions. | |
| 5871 | + const glm::vec3 UNIT_DIMENSIONS = glm::vec3(1.0f, 1.0f, 1.0f); | |
| 5872 | + dimensions = UNIT_DIMENSIONS; | |
| 5873 | + qInfo(interfaceapp) << "Asset" << name << "auto-resize timed out; resized to " << dimensions; | |
| 5874 | + doResize = true; | |
| 5875 | + | |
| 5876 | + item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next. | |
| 5877 | + } else { | |
| 5878 | + // No action on this entity; advance to next. | |
| 5879 | + ++item; | |
| 5880 | + } | |
| 5881 | + } | |
| 5882 | + | |
| 5883 | + if (doResize) { | |
| 5884 | + EntityItemProperties properties; | |
| 5885 | + properties.setDimensions(dimensions); | |
| 5886 | + properties.setVisible(true); | |
| 5887 | + properties.setCollisionless(false); | |
| 5888 | + properties.setUserData(GRABBABLE_USER_DATA); | |
| 5889 | + properties.setLastEdited(usecTimestampNow()); | |
| 5890 | + entityScriptingInterface->editEntity(entityID, properties); | |
| 5891 | + } | |
| 5892 | + } | |
| 5893 | + | |
| 5894 | + // Stop timer if nothing in list to check. | |
| 5895 | + if (_addAssetToWorldResizeList.size() == 0) { | |
| 5896 | + _addAssetToWorldResizeTimer.stop(); | |
| 5897 | + } | |
| 5898 | +} | |
| 5899 | + | |
| 5900 | + | |
| 5901 | +void Application::addAssetToWorldInfo(QString modelName, QString infoText) { | |
| 5902 | + // Displays the most recent info message, subject to being overridden by error messages. | |
| 5903 | + | |
| 5904 | + if (_aboutToQuit) { | |
| 5905 | + return; | |
| 5906 | + } | |
| 5907 | + | |
| 5908 | + /* | |
| 5909 | + Cancel info timer if running. | |
| 5910 | + If list has an entry for modelName, delete it (just one). | |
| 5911 | + Append modelName, infoText to list. | |
| 5912 | + Display infoText in message box unless an error is being displayed (i.e., error timer is running). | |
| 5913 | + Show message box if not already visible. | |
| 5914 | + */ | |
| 5915 | + | |
| 5916 | + _addAssetToWorldInfoTimer.stop(); | |
| 5917 | + | |
| 5918 | + addAssetToWorldInfoClear(modelName); | |
| 5919 | + | |
| 5920 | + _addAssetToWorldInfoKeys.append(modelName); | |
| 5921 | + _addAssetToWorldInfoMessages.append(infoText); | |
| 5922 | + | |
| 5923 | + if (!_addAssetToWorldErrorTimer.isActive()) { | |
| 5924 | + if (!_addAssetToWorldMessageBox) { | |
| 5925 | + _addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, | |
| 5926 | + "Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton); | |
| 5927 | + connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); | |
| 5928 | + } | |
| 5929 | + | |
| 5930 | + _addAssetToWorldMessageBox->setProperty("text", "\n" + infoText); | |
| 5931 | + _addAssetToWorldMessageBox->setVisible(true); | |
| 5932 | + } | |
| 5933 | +} | |
| 5934 | + | |
| 5935 | +void Application::addAssetToWorldInfoClear(QString modelName) { | |
| 5936 | + // Clears modelName entry from message list without affecting message currently displayed. | |
| 5937 | + | |
| 5938 | + if (_aboutToQuit) { | |
| 5939 | + return; | |
| 5940 | + } | |
| 5941 | + | |
| 5942 | + /* | |
| 5943 | + Delete entry for modelName from list. | |
| 5944 | + */ | |
| 5945 | + | |
| 5946 | + auto index = _addAssetToWorldInfoKeys.indexOf(modelName); | |
| 5947 | + if (index > -1) { | |
| 5948 | + _addAssetToWorldInfoKeys.removeAt(index); | |
| 5949 | + _addAssetToWorldInfoMessages.removeAt(index); | |
| 5950 | + } | |
| 5951 | +} | |
| 5952 | + | |
| 5953 | +void Application::addAssetToWorldInfoDone(QString modelName) { | |
| 5954 | + // Continues to display this message if the latest for a few seconds, then deletes it and displays the next latest. | |
| 5955 | + | |
| 5956 | + if (_aboutToQuit) { | |
| 5957 | + return; | |
| 5958 | + } | |
| 5959 | + | |
| 5960 | + /* | |
| 5961 | + Delete entry for modelName from list. | |
| 5962 | + (Re)start the info timer to update message box. ... onAddAssetToWorldInfoTimeout() | |
| 5963 | + */ | |
| 5964 | + | |
| 5965 | + addAssetToWorldInfoClear(modelName); | |
| 5966 | + _addAssetToWorldInfoTimer.start(); | |
| 5967 | +} | |
| 5968 | + | |
| 5969 | +void Application::addAssetToWorldInfoTimeout() { | |
| 5970 | + if (_aboutToQuit) { | |
| 5971 | + return; | |
| 5972 | + } | |
| 5973 | + | |
| 5974 | + /* | |
| 5975 | + If list not empty, display last message in list (may already be displayed ) unless an error is being displayed. | |
| 5976 | + If list empty, close the message box unless an error is being displayed. | |
| 5977 | + */ | |
| 5978 | + | |
| 5979 | + if (!_addAssetToWorldErrorTimer.isActive() && _addAssetToWorldMessageBox) { | |
| 5980 | + if (_addAssetToWorldInfoKeys.length() > 0) { | |
| 5981 | + _addAssetToWorldMessageBox->setProperty("text", "\n" + _addAssetToWorldInfoMessages.last()); | |
| 5982 | + } else { | |
| 5983 | + disconnect(_addAssetToWorldMessageBox); | |
| 5984 | + _addAssetToWorldMessageBox->setVisible(false); | |
| 5985 | + _addAssetToWorldMessageBox->deleteLater(); | |
| 5986 | + _addAssetToWorldMessageBox = nullptr; | |
| 5987 | + } | |
| 5988 | + } | |
| 5989 | +} | |
| 5990 | + | |
| 5991 | + | |
| 5992 | +void Application::addAssetToWorldError(QString modelName, QString errorText) { | |
| 5993 | + // Displays the most recent error message for a few seconds. | |
| 5994 | + | |
| 5995 | + if (_aboutToQuit) { | |
| 5996 | + return; | |
| 5997 | + } | |
| 5998 | + | |
| 5999 | + /* | |
| 6000 | + If list has an entry for modelName, delete it. | |
| 6001 | + Display errorText in message box. | |
| 6002 | + Show message box if not already visible. | |
| 6003 | + (Re)start error timer. ... onAddAssetToWorldErrorTimeout() | |
| 6004 | + */ | |
| 6005 | + | |
| 6006 | + addAssetToWorldInfoClear(modelName); | |
| 6007 | + | |
| 6008 | + if (!_addAssetToWorldMessageBox) { | |
| 6009 | + _addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, | |
| 6010 | + "Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton); | |
| 6011 | + connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); | |
| 6012 | + } | |
| 6013 | + | |
| 6014 | + _addAssetToWorldMessageBox->setProperty("text", "\n" + errorText); | |
| 6015 | + _addAssetToWorldMessageBox->setVisible(true); | |
| 6016 | + | |
| 6017 | + _addAssetToWorldErrorTimer.start(); | |
| 6018 | +} | |
| 6019 | + | |
| 6020 | +void Application::addAssetToWorldErrorTimeout() { | |
| 6021 | + if (_aboutToQuit) { | |
| 6022 | + return; | |
| 6023 | + } | |
| 6024 | + | |
| 6025 | + /* | |
| 6026 | + If list is not empty, display message from last entry. | |
| 6027 | + If list is empty, close the message box. | |
| 6028 | + */ | |
| 6029 | + | |
| 6030 | + if (_addAssetToWorldMessageBox) { | |
| 6031 | + if (_addAssetToWorldInfoKeys.length() > 0) { | |
| 6032 | + _addAssetToWorldMessageBox->setProperty("text", "\n" + _addAssetToWorldInfoMessages.last()); | |
| 6033 | + } else { | |
| 6034 | + disconnect(_addAssetToWorldMessageBox); | |
| 6035 | + _addAssetToWorldMessageBox->setVisible(false); | |
| 6036 | + _addAssetToWorldMessageBox->deleteLater(); | |
| 6037 | + _addAssetToWorldMessageBox = nullptr; | |
| 6038 | + } | |
| 6039 | + } | |
| 6040 | +} | |
| 6041 | + | |
| 6042 | + | |
| 6043 | +void Application::addAssetToWorldMessageClose() { | |
| 6044 | + // Clear messages, e.g., if Interface is being closed or domain changes. | |
| 6045 | + | |
| 6046 | + /* | |
| 6047 | + Call if user manually closes message box. | |
| 6048 | + Call if domain changes. | |
| 6049 | + Call if application is shutting down. | |
| 6050 | + | |
| 6051 | + Stop timers. | |
| 6052 | + Close the message box if open. | |
| 6053 | + Clear lists. | |
| 6054 | + */ | |
| 6055 | + | |
| 6056 | + _addAssetToWorldInfoTimer.stop(); | |
| 6057 | + _addAssetToWorldErrorTimer.stop(); | |
| 6058 | + | |
| 6059 | + if (_addAssetToWorldMessageBox) { | |
| 6060 | + disconnect(_addAssetToWorldMessageBox); | |
| 6061 | + _addAssetToWorldMessageBox->setVisible(false); | |
| 6062 | + _addAssetToWorldMessageBox->deleteLater(); | |
| 6063 | + _addAssetToWorldMessageBox = nullptr; | |
| 6064 | + } | |
| 6065 | + | |
| 6066 | + _addAssetToWorldInfoKeys.clear(); | |
| 6067 | + _addAssetToWorldInfoMessages.clear(); | |
| 6068 | +} | |
| 6069 | + | |
| 6070 | +void Application::onAssetToWorldMessageBoxClosed() { | |
| 6071 | + if (_addAssetToWorldMessageBox) { | |
| 6072 | + // User manually closed message box; perhaps because it has become stuck, so reset all messages. | |
| 6073 | + qInfo(interfaceapp) << "User manually closed download status message box"; | |
| 6074 | + disconnect(_addAssetToWorldMessageBox); | |
| 6075 | + _addAssetToWorldMessageBox = nullptr; | |
| 6076 | + addAssetToWorldMessageClose(); | |
| 6077 | + } | |
| 6078 | +} | |
| 6079 | + | |
| 6080 | + | |
| 6081 | +void Application::handleUnzip(QString zipFile, QString unzipFile, bool autoAdd) { | |
| 6082 | + if (autoAdd) { | |
| 6083 | + if (!unzipFile.isEmpty()) { | |
| 6084 | + addAssetToWorld(unzipFile); | |
| 6085 | + } else { | |
| 6086 | + addAssetToWorldUnzipFailure(zipFile); | |
| 6087 | + } | |
| 6088 | + } else { | |
| 6089 | + showAssetServerWidget(unzipFile); | |
| 6090 | + } | |
| 6091 | +} | |
| 6092 | + | |
| 5471 | 6093 | void Application::packageModel() { |
| 5472 | 6094 | ModelPackager::package(); |
| 5473 | 6095 | } |