Showing the changes to interface/src/Application.cpp (1 of 179 files). Show all
Unified · Split
| @@ -42,16 +42,20 @@ · 41 unmodified lines | |||
| 42 | #include <QtMultimedia/QMediaPlayer> | 42 | #include <QtMultimedia/QMediaPlayer> |
| 43 | 43 | ||
| 44 | #include <QProcessEnvironment> | 44 | #include <QProcessEnvironment> |
| 45 | #include <QTemporaryDir> | ||
| 45 | 46 | ||
| 46 | #include <gl/QOpenGLContextWrapper.h> | 47 | #include <gl/QOpenGLContextWrapper.h> |
| 47 | 48 | ||
| 48 | #include <shared/GlobalAppProperties.h> | 49 | #include <shared/GlobalAppProperties.h> |
| 50 | #include <StatTracker.h> | ||
| 51 | #include <Trace.h> | ||
| 49 | #include <ResourceScriptingInterface.h> | 52 | #include <ResourceScriptingInterface.h> |
| 50 | #include <AccountManager.h> | 53 | #include <AccountManager.h> |
| 51 | #include <AddressManager.h> | 54 | #include <AddressManager.h> |
| 52 | #include <AnimDebugDraw.h> | 55 | #include <AnimDebugDraw.h> |
| 53 | #include <BuildInfo.h> | 56 | #include <BuildInfo.h> |
| 54 | #include <AssetClient.h> | 57 | #include <AssetClient.h> |
| 58 | #include <AssetUpload.h> | ||
| 55 | #include <AutoUpdater.h> | 59 | #include <AutoUpdater.h> |
| 56 | #include <AudioInjectorManager.h> | 60 | #include <AudioInjectorManager.h> |
| 57 | #include <CursorManager.h> | 61 | #include <CursorManager.h> |
| @@ -77,6 +81,7 @@ · 19 unmodified lines | |||
| 77 | #include <UserActivityLoggerScriptingInterface.h> | 81 | #include <UserActivityLoggerScriptingInterface.h> |
| 78 | #include <LogHandler.h> | 82 | #include <LogHandler.h> |
| 79 | #include <MainWindow.h> | 83 | #include <MainWindow.h> |
| 84 | #include <MappingRequest.h> | ||
| 80 | #include <MessagesClient.h> | 85 | #include <MessagesClient.h> |
| 81 | #include <ModelEntityItem.h> | 86 | #include <ModelEntityItem.h> |
| 82 | #include <NetworkAccessManager.h> | 87 | #include <NetworkAccessManager.h> |
| @@ -156,6 +161,7 @@ · 73 unmodified lines | |||
| 156 | #include "ui/DialogsManager.h" | 161 | #include "ui/DialogsManager.h" |
| 157 | #include "ui/LoginDialog.h" | 162 | #include "ui/LoginDialog.h" |
| 158 | #include "ui/overlays/Cube3DOverlay.h" | 163 | #include "ui/overlays/Cube3DOverlay.h" |
| 164 | #include "ui/overlays/Web3DOverlay.h" | ||
| 159 | #include "ui/Snapshot.h" | 165 | #include "ui/Snapshot.h" |
| 160 | #include "ui/SnapshotAnimated.h" | 166 | #include "ui/SnapshotAnimated.h" |
| 161 | #include "ui/StandAloneJSConsole.h" | 167 | #include "ui/StandAloneJSConsole.h" |
| @@ -178,6 +184,8 @@ extern "C" { · 16 unmodified lines | |||
| 178 | } | 184 | } |
| 179 | #endif | 185 | #endif |
| 180 | 186 | ||
| 187 | Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse") | ||
| 188 | |||
| 181 | using namespace std; | 189 | using namespace std; |
| 182 | 190 | ||
| 183 | static QTimer locationUpdateTimer; | 191 | static QTimer locationUpdateTimer; |
| @@ -424,6 +432,8 @@ bool setupEssentials(int& argc, char** argv) { · 240 unmodified lines | |||
| 424 | steamClient->init(); | 432 | steamClient->init(); |
| 425 | } | 433 | } |
| 426 | 434 | ||
| 435 | DependencyManager::set<tracing::Tracer>(); | ||
| 436 | |||
| 427 | #if defined(Q_OS_WIN) | 437 | #if defined(Q_OS_WIN) |
| 428 | // Select appropriate audio DLL | 438 | // Select appropriate audio DLL |
| 429 | QString audioDLLPath = QCoreApplication::applicationDirPath(); | 439 | QString audioDLLPath = QCoreApplication::applicationDirPath(); |
| @@ -446,6 +456,7 @@ bool setupEssentials(int& argc, char** argv) { · 16 unmodified lines | |||
| 446 | 456 | ||
| 447 | // Set dependencies | 457 | // Set dependencies |
| 448 | DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp)); | 458 | DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp)); |
| 459 | DependencyManager::set<StatTracker>(); | ||
| 449 | DependencyManager::set<ScriptEngines>(); | 460 | DependencyManager::set<ScriptEngines>(); |
| 450 | DependencyManager::set<Preferences>(); | 461 | DependencyManager::set<Preferences>(); |
| 451 | DependencyManager::set<recording::Deck>(); | 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 | const QString TEST_SCRIPT = "--testScript"; | 572 | const QString TEST_SCRIPT = "--testScript"; |
| 573 | const QString TRACE_FILE = "--traceFile"; | ||
| 562 | const QStringList args = arguments(); | 574 | const QStringList args = arguments(); |
| 563 | for (int i = 0; i < args.size() - 1; ++i) { | 575 | for (int i = 0; i < args.size() - 1; ++i) { |
| 564 | if (args.at(i) == TEST_SCRIPT) { | 576 | if (args.at(i) == TEST_SCRIPT) { |
| @@ -566,6 +578,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 1 unmodified line | |||
| 566 | if (QFileInfo(testScriptPath).exists()) { | 578 | if (QFileInfo(testScriptPath).exists()) { |
| 567 | setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); | 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 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | 1196 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); |
| 1181 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, | 1197 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, |
| 1182 | [this](const EntityItemID& entityItemID, const PointerEvent& event) { | 1198 | [this](const EntityItemID& entityItemID, const PointerEvent& event) { |
| 1199 | setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | ||
| 1183 | setKeyboardFocusEntity(entityItemID); | 1200 | setKeyboardFocusEntity(entityItemID); |
| 1184 | }); | 1201 | }); |
| 1185 | 1202 | ||
| 1186 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) { | 1203 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) { |
| 1187 | if (entityItemID == _keyboardFocusedItem.get()) { | 1204 | if (entityItemID == _keyboardFocusedEntity.get()) { |
| 1188 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); | 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 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); | 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 | connect(this, &Application::aboutToQuit, [=]() { | 1232 | connect(this, &Application::aboutToQuit, [=]() { |
| 1233 | setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | ||
| 1198 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); | 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 | qCDebug(interfaceapp) << "Server content version: " << contentVersion; | 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 | Setting::Handle<bool> firstRun { Settings::firstRun, true }; | 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 | Setting::Handle<bool> tutorialComplete { "tutorialComplete", false }; | 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 | qCDebug(interfaceapp) << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName(); | 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 | qCDebug(interfaceapp) << "Tutorial complete: " << tutorialComplete.get(); | 1445 | qCDebug(interfaceapp) << "Tutorial complete: " << tutorialComplete.get(); |
| 1395 | qCDebug(interfaceapp) << "Should go to tutorial: " << shouldGoToTutorial; | 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 | // After all of the constructor is completed, then set firstRun to false. | 1502 | // After all of the constructor is completed, then set firstRun to false. |
| 1452 | firstRun.set(false); | 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 | void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { | 1525 | void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { |
| @@ -1579,6 +1648,13 @@ void Application::cleanupBeforeQuit() { · 122 unmodified lines | |||
| 1579 | QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false"); | 1648 | QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false"); |
| 1580 | qCDebug(interfaceapp) << "QTWEBENGINE_REMOTE_DEBUGGING =" << webengineRemoteDebugging; | 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 | // Stop third party processes so that they're not left running in the event of a subsequent shutdown crash. | 1658 | // Stop third party processes so that they're not left running in the event of a subsequent shutdown crash. |
| 1583 | #ifdef HAVE_DDE | 1659 | #ifdef HAVE_DDE |
| 1584 | DependencyManager::get<DdeFaceTracker>()->setEnabled(false); | 1660 | DependencyManager::get<DdeFaceTracker>()->setEnabled(false); |
| @@ -1753,7 +1829,7 @@ void Application::initializeGL() { · 168 unmodified lines | |||
| 1753 | // Set up the render engine | 1829 | // Set up the render engine |
| 1754 | render::CullFunctor cullFunctor = LODManager::shouldRender; | 1830 | render::CullFunctor cullFunctor = LODManager::shouldRender; |
| 1755 | _renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor); | 1831 | _renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor); |
| 1756 | static const QString RENDER_FORWARD = "RENDER_FORWARD"; | 1832 | static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; |
| 1757 | if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { | 1833 | if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) { |
| 1758 | _renderEngine->addJob<RenderForwardTask>("RenderForwardTask", cullFunctor); | 1834 | _renderEngine->addJob<RenderForwardTask>("RenderForwardTask", cullFunctor); |
| 1759 | } else { | 1835 | } else { |
| @@ -1838,9 +1914,9 @@ void Application::initializeUi() { · 78 unmodified lines | |||
| 1838 | rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data()); | 1914 | rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data()); |
| 1839 | rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data()); | 1915 | rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data()); |
| 1840 | rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data()); | 1916 | rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data()); |
| 1841 | FileScriptingInterface* fileDownload = new FileScriptingInterface(engine); | 1917 | _fileDownload = new FileScriptingInterface(engine); |
| 1842 | rootContext->setContextProperty("File", fileDownload); | 1918 | rootContext->setContextProperty("File", _fileDownload); |
| 1843 | connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::showAssetServerWidget); | 1919 | connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip); |
| 1844 | rootContext->setContextProperty("MyAvatar", getMyAvatar().get()); | 1920 | rootContext->setContextProperty("MyAvatar", getMyAvatar().get()); |
| 1845 | rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data()); | 1921 | rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data()); |
| 1846 | rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data()); | 1922 | rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data()); |
| @@ -1856,6 +1932,7 @@ void Application::initializeUi() { · 9 unmodified lines | |||
| 1856 | rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface()); | 1932 | rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface()); |
| 1857 | 1933 | ||
| 1858 | rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data()); | 1934 | rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data()); |
| 1935 | rootContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data()); | ||
| 1859 | 1936 | ||
| 1860 | rootContext->setContextProperty("Camera", &_myCamera); | 1937 | rootContext->setContextProperty("Camera", &_myCamera); |
| 1861 | 1938 | ||
| @@ -1945,7 +2022,7 @@ void Application::paintGL() { · 83 unmodified lines | |||
| 1945 | _frameCount++; | 2022 | _frameCount++; |
| 1946 | 2023 | ||
| 1947 | auto lastPaintBegin = usecTimestampNow(); | 2024 | auto lastPaintBegin = usecTimestampNow(); |
| 1948 | PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); | 2025 | PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); |
| 1949 | PerformanceTimer perfTimer("paintGL"); | 2026 | PerformanceTimer perfTimer("paintGL"); |
| 1950 | 2027 | ||
| 1951 | if (nullptr == _displayPlugin) { | 2028 | if (nullptr == _displayPlugin) { |
| @@ -2122,7 +2199,7 @@ void Application::paintGL() { · 170 unmodified lines | |||
| 2122 | auto finalFramebuffer = framebufferCache->getFramebuffer(); | 2199 | auto finalFramebuffer = framebufferCache->getFramebuffer(); |
| 2123 | 2200 | ||
| 2124 | { | 2201 | { |
| 2125 | PROFILE_RANGE(__FUNCTION__ "/mainRender"); | 2202 | PROFILE_RANGE(render, "/mainRender"); |
| 2126 | PerformanceTimer perfTimer("mainRender"); | 2203 | PerformanceTimer perfTimer("mainRender"); |
| 2127 | renderArgs._boomOffset = boomOffset; | 2204 | renderArgs._boomOffset = boomOffset; |
| 2128 | // Viewport is assigned to the size of the framebuffer | 2205 | // Viewport is assigned to the size of the framebuffer |
| @@ -2177,7 +2254,7 @@ void Application::paintGL() { · 48 unmodified lines | |||
| 2177 | frame->overlay = _applicationOverlay.getOverlayTexture(); | 2254 | frame->overlay = _applicationOverlay.getOverlayTexture(); |
| 2178 | // deliver final scene rendering commands to the display plugin | 2255 | // deliver final scene rendering commands to the display plugin |
| 2179 | { | 2256 | { |
| 2180 | PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); | 2257 | PROFILE_RANGE(render, "/pluginOutput"); |
| 2181 | PerformanceTimer perfTimer("pluginOutput"); | 2258 | PerformanceTimer perfTimer("pluginOutput"); |
| 2182 | _frameCounter.increment(); | 2259 | _frameCounter.increment(); |
| 2183 | displayPlugin->submitFrame(frame); | 2260 | displayPlugin->submitFrame(frame); |
| @@ -2265,7 +2342,7 @@ void Application::resizeEvent(QResizeEvent* event) { · 81 unmodified lines | |||
| 2265 | } | 2342 | } |
| 2266 | 2343 | ||
| 2267 | void Application::resizeGL() { | 2344 | void Application::resizeGL() { |
| 2268 | PROFILE_RANGE(__FUNCTION__); | 2345 | PROFILE_RANGE(render, __FUNCTION__); |
| 2269 | if (nullptr == _displayPlugin) { | 2346 | if (nullptr == _displayPlugin) { |
| 2270 | return; | 2347 | return; |
| 2271 | } | 2348 | } |
| @@ -2320,7 +2397,6 @@ bool Application::importSVOFromURL(const QString& urlString) { · 48 unmodified lines | |||
| 2320 | } | 2397 | } |
| 2321 | 2398 | ||
| 2322 | bool Application::event(QEvent* event) { | 2399 | bool Application::event(QEvent* event) { |
| 2323 | |||
| 2324 | if (!Menu::getInstance()) { | 2400 | if (!Menu::getInstance()) { |
| 2325 | return false; | 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 | switch (event->type()) { | 2448 | switch (event->type()) { |
| 2373 | case QEvent::KeyPress: | 2449 | case QEvent::KeyPress: |
| 2374 | case QEvent::KeyRelease: { | 2450 | case QEvent::KeyRelease: { |
| 2375 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | 2451 | //auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); |
| 2376 | auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get()); | 2452 | auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get()); |
| 2377 | if (entity && entity->getEventHandler()) { | 2453 | if (entity && entity->getEventHandler()) { |
| 2378 | event->setAccepted(false); | 2454 | event->setAccepted(false); |
| 2379 | QCoreApplication::sendEvent(entity->getEventHandler(), event); | 2455 | QCoreApplication::sendEvent(entity->getEventHandler(), event); |
| 2380 | if (event->isAccepted()) { | 2456 | if (event->isAccepted()) { |
| 2381 | _lastAcceptedKeyPress = usecTimestampNow(); | 2457 | _lastAcceptedKeyPress = usecTimestampNow(); |
| 2382 | return true; | 2458 | return true; |
| 2459 | } | ||
| 2383 | } | 2460 | } |
| 2461 | break; | ||
| 2384 | } | 2462 | } |
| 2385 | break; | 2463 | default: |
| 2386 | } | 2464 | break; |
| 2465 | } | ||
| 2466 | } | ||
| 2467 | } | ||
| 2387 | 2468 | ||
| 2388 | default: | 2469 | { |
| 2389 | break; | 2470 | if (_keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) { |
| 2390 | } | 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 | void Application::mouseMoveEvent(QMouseEvent* event) { | 3012 | void Application::mouseMoveEvent(QMouseEvent* event) { |
| 2914 | PROFILE_RANGE(__FUNCTION__); | 3013 | PROFILE_RANGE(app_input_mouse, __FUNCTION__); |
| 2915 | 3014 | ||
| 2916 | if (_aboutToQuit) { | 3015 | if (_aboutToQuit) { |
| 2917 | return; | 3016 | return; |
| @@ -2947,6 +3046,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 29 unmodified lines | |||
| 2947 | 3046 | ||
| 2948 | if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || | 3047 | if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || |
| 2949 | getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { | 3048 | getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { |
| 3049 | getOverlays().mouseMoveEvent(&mappedEvent); | ||
| 2950 | getEntities()->mouseMoveEvent(&mappedEvent); | 3050 | getEntities()->mouseMoveEvent(&mappedEvent); |
| 2951 | } | 3051 | } |
| 2952 | _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts | 3052 | _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts |
| @@ -2959,7 +3059,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 6 unmodified lines | |||
| 2959 | if (_keyboardMouseDevice->isActive()) { | 3059 | if (_keyboardMouseDevice->isActive()) { |
| 2960 | _keyboardMouseDevice->mouseMoveEvent(event); | 3060 | _keyboardMouseDevice->mouseMoveEvent(event); |
| 2961 | } | 3061 | } |
| 2962 | |||
| 2963 | } | 3062 | } |
| 2964 | 3063 | ||
| 2965 | void Application::mousePressEvent(QMouseEvent* event) { | 3064 | void Application::mousePressEvent(QMouseEvent* event) { |
| @@ -2981,6 +3080,7 @@ void Application::mousePressEvent(QMouseEvent* event) { · 15 unmodified lines | |||
| 2981 | event->buttons(), event->modifiers()); | 3080 | event->buttons(), event->modifiers()); |
| 2982 | 3081 | ||
| 2983 | if (!_aboutToQuit) { | 3082 | if (!_aboutToQuit) { |
| 3083 | getOverlays().mousePressEvent(&mappedEvent); | ||
| 2984 | getEntities()->mousePressEvent(&mappedEvent); | 3084 | getEntities()->mousePressEvent(&mappedEvent); |
| 2985 | } | 3085 | } |
| 2986 | 3086 | ||
| @@ -2991,7 +3091,6 @@ void Application::mousePressEvent(QMouseEvent* event) { · 4 unmodified lines | |||
| 2991 | return; | 3091 | return; |
| 2992 | } | 3092 | } |
| 2993 | 3093 | ||
| 2994 | |||
| 2995 | if (hasFocus()) { | 3094 | if (hasFocus()) { |
| 2996 | if (_keyboardMouseDevice->isActive()) { | 3095 | if (_keyboardMouseDevice->isActive()) { |
| 2997 | _keyboardMouseDevice->mousePressEvent(event); | 3096 | _keyboardMouseDevice->mousePressEvent(event); |
| @@ -3002,7 +3101,6 @@ void Application::mousePressEvent(QMouseEvent* event) { · 4 unmodified lines | |||
| 3002 | HFActionEvent actionEvent(HFActionEvent::startType(), | 3101 | HFActionEvent actionEvent(HFActionEvent::startType(), |
| 3003 | computePickRay(mappedEvent.x(), mappedEvent.y())); | 3102 | computePickRay(mappedEvent.x(), mappedEvent.y())); |
| 3004 | sendEvent(this, &actionEvent); | 3103 | sendEvent(this, &actionEvent); |
| 3005 | |||
| 3006 | } | 3104 | } |
| 3007 | } | 3105 | } |
| 3008 | } | 3106 | } |
| @@ -3027,6 +3125,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { · 18 unmodified lines | |||
| 3027 | event->buttons(), event->modifiers()); | 3125 | event->buttons(), event->modifiers()); |
| 3028 | 3126 | ||
| 3029 | if (!_aboutToQuit) { | 3127 | if (!_aboutToQuit) { |
| 3128 | getOverlays().mouseReleaseEvent(&mappedEvent); | ||
| 3030 | getEntities()->mouseReleaseEvent(&mappedEvent); | 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 | void Application::idle(float nsecsElapsed) { | 3306 | void Application::idle(float nsecsElapsed) { |
| 3307 | PerformanceTimer perfTimer("idle"); | ||
| 3208 | 3308 | ||
| 3209 | // Update the deadlock watchdog | 3309 | // Update the deadlock watchdog |
| 3210 | updateHeartbeat(); | 3310 | updateHeartbeat(); |
| @@ -3219,7 +3319,17 @@ void Application::idle(float nsecsElapsed) { · 8 unmodified lines | |||
| 3219 | connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); | 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 | if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { | 3334 | if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { |
| 3225 | steamClient->runCallbacks(); | 3335 | steamClient->runCallbacks(); |
| @@ -3241,8 +3351,6 @@ void Application::idle(float nsecsElapsed) { · 15 unmodified lines | |||
| 3241 | 3351 | ||
| 3242 | _simCounter.increment(); | 3352 | _simCounter.increment(); |
| 3243 | 3353 | ||
| 3244 | PerformanceTimer perfTimer("idle"); | ||
| 3245 | |||
| 3246 | // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing | 3354 | // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing |
| 3247 | // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing | 3355 | // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing |
| 3248 | // details normally. | 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 | const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus | 3371 | const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus |
| 3264 | quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; | 3372 | quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; |
| 3265 | if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { | 3373 | if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { |
| 3266 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); | 3374 | setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); |
| 3375 | setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); | ||
| 3267 | } else { | 3376 | } else { |
| 3268 | // update position of highlight overlay | 3377 | // update position of highlight overlay |
| 3269 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | 3378 | if (!_keyboardFocusedEntity.get().isInvalidID()) { |
| 3270 | auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get()); | 3379 | auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get()); |
| 3271 | if (entity && _keyboardFocusHighlight) { | 3380 | if (entity && _keyboardFocusHighlight) { |
| 3272 | _keyboardFocusHighlight->setRotation(entity->getRotation()); | 3381 | _keyboardFocusHighlight->setRotation(entity->getRotation()); |
| 3273 | _keyboardFocusHighlight->setPosition(entity->getPosition()); | 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 | QUuid Application::getKeyboardFocusEntity() const { | 4002 | QUuid Application::getKeyboardFocusEntity() const { |
| 3862 | return _keyboardFocusedItem.get(); | 4003 | return _keyboardFocusedEntity.get(); |
| 3863 | } | 4004 | } |
| 3864 | 4005 | ||
| 3865 | void Application::setKeyboardFocusEntity(QUuid id) { | 4006 | void Application::setKeyboardFocusEntity(QUuid id) { |
| @@ -3867,21 +4008,21 @@ void Application::setKeyboardFocusEntity(QUuid id) { · 1 unmodified line | |||
| 3867 | setKeyboardFocusEntity(entityItemID); | 4008 | setKeyboardFocusEntity(entityItemID); |
| 3868 | } | 4009 | } |
| 3869 | 4010 | ||
| 4011 | static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f; | ||
| 4012 | |||
| 3870 | void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { | 4013 | void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { |
| 3871 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | 4014 | if (_keyboardFocusedEntity.get() != entityItemID) { |
| 3872 | if (_keyboardFocusedItem.get() != entityItemID) { | 4015 | _keyboardFocusedEntity.set(entityItemID); |
| 3873 | // reset focused entity | 4016 | |
| 3874 | _keyboardFocusedItem.set(UNKNOWN_ENTITY_ID); | 4017 | if (_keyboardFocusHighlight && _keyboardFocusedOverlay.get() == UNKNOWN_OVERLAY_ID) { |
| 3875 | if (_keyboardFocusHighlight) { | ||
| 3876 | _keyboardFocusHighlight->setVisible(false); | 4018 | _keyboardFocusHighlight->setVisible(false); |
| 3877 | } | 4019 | } |
| 3878 | 4020 | ||
| 3879 | // if invalid, return without expensive (locking) operations | ||
| 3880 | if (entityItemID == UNKNOWN_ENTITY_ID) { | 4021 | if (entityItemID == UNKNOWN_ENTITY_ID) { |
| 3881 | return; | 4022 | return; |
| 3882 | } | 4023 | } |
| 3883 | 4024 | ||
| 3884 | // if valid, query properties | 4025 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); |
| 3885 | auto properties = entityScriptingInterface->getEntityProperties(entityItemID); | 4026 | auto properties = entityScriptingInterface->getEntityProperties(entityItemID); |
| 3886 | if (!properties.getLocked() && properties.getVisible()) { | 4027 | if (!properties.getLocked() && properties.getVisible()) { |
| 3887 | auto entity = getEntities()->getTree()->findEntityByID(entityItemID); | 4028 | auto entity = getEntities()->getTree()->findEntityByID(entityItemID); |
| @@ -3890,30 +4031,45 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { · 2 unmodified lines | |||
| 3890 | if (_keyboardMouseDevice->isActive()) { | 4031 | if (_keyboardMouseDevice->isActive()) { |
| 3891 | _keyboardMouseDevice->pluginFocusOutEvent(); | 4032 | _keyboardMouseDevice->pluginFocusOutEvent(); |
| 3892 | } | 4033 | } |
| 3893 | _keyboardFocusedItem.set(entityItemID); | ||
| 3894 | _lastAcceptedKeyPress = usecTimestampNow(); | 4034 | _lastAcceptedKeyPress = usecTimestampNow(); |
| 3895 | 4035 | ||
| 3896 | // create a focus | 4036 | setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(), |
| 3897 | if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { | 4037 | entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR); |
| 3898 | _keyboardFocusHighlight = std::make_shared<Cube3DOverlay>(); | 4038 | } |
| 3899 | _keyboardFocusHighlight->setAlpha(1.0f); | 4039 | } |
| 3900 | _keyboardFocusHighlight->setBorderSize(1.0f); | 4040 | } |
| 3901 | _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); | 4041 | } |
| 3902 | _keyboardFocusHighlight->setIsSolid(false); | 4042 | |
| 3903 | _keyboardFocusHighlight->setPulseMin(0.5); | 4043 | unsigned int Application::getKeyboardFocusOverlay() { |
| 3904 | _keyboardFocusHighlight->setPulseMax(1.0); | 4044 | return _keyboardFocusedOverlay.get(); |
| 3905 | _keyboardFocusHighlight->setColorPulse(1.0); | 4045 | } |
| 3906 | _keyboardFocusHighlight->setIgnoreRayIntersection(true); | 4046 | |
| 3907 | _keyboardFocusHighlight->setDrawInFront(false); | 4047 | void Application::setKeyboardFocusOverlay(unsigned int overlayID) { |
| 3908 | _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); | 4048 | if (overlayID != _keyboardFocusedOverlay.get()) { |
| 3909 | } | 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 | 4065 | if (_keyboardMouseDevice->isActive()) { |
| 3912 | _keyboardFocusHighlight->setRotation(entity->getRotation()); | 4066 | _keyboardMouseDevice->pluginFocusOutEvent(); |
| 3913 | _keyboardFocusHighlight->setPosition(entity->getPosition()); | ||
| 3914 | _keyboardFocusHighlight->setDimensions(entity->getDimensions() * 1.05f); | ||
| 3915 | _keyboardFocusHighlight->setVisible(true); | ||
| 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 | void Application::update(float deltaTime) { | 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 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); | 4101 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); |
| 3944 | PerformanceWarning warn(showWarnings, "Application::update()"); | 4102 | PerformanceWarning warn(showWarnings, "Application::update()"); |
| @@ -3946,6 +4104,11 @@ void Application::update(float deltaTime) { · 1 unmodified line | |||
| 3946 | updateLOD(); | 4104 | updateLOD(); |
| 3947 | 4105 | ||
| 3948 | if (!_physicsEnabled) { | 4106 | if (!_physicsEnabled) { |
| 4107 | if (!domainLoadingInProgress) { | ||
| 4108 | PROFILE_ASYNC_BEGIN(app, "Scene Loading", ""); | ||
| 4109 | domainLoadingInProgress = true; | ||
| 4110 | } | ||
| 4111 | |||
| 3949 | // we haven't yet enabled physics. we wait until we think we have all the collision information | 4112 | // we haven't yet enabled physics. we wait until we think we have all the collision information |
| 3950 | // for nearby entities before starting bullet up. | 4113 | // for nearby entities before starting bullet up. |
| 3951 | quint64 now = usecTimestampNow(); | 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 | QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>(); | 4234 | QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>(); |
| 4069 | 4235 | ||
| 4070 | if (_physicsEnabled) { | 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 | PerformanceTimer perfTimer("physics"); | 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 | PerformanceTimer perfTimer("updateStates)"); | 4244 | PerformanceTimer perfTimer("updateStates)"); |
| 4079 | static VectorOfMotionStates motionStates; | 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 | PerformanceTimer perfTimer("stepSimulation"); | 4277 | PerformanceTimer perfTimer("stepSimulation"); |
| 4112 | getEntities()->getTree()->withWriteLock([&] { | 4278 | getEntities()->getTree()->withWriteLock([&] { |
| 4113 | _physicsEngine->stepSimulation(); | 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 | PerformanceTimer perfTimer("harvestChanges"); | 4284 | PerformanceTimer perfTimer("harvestChanges"); |
| 4119 | if (_physicsEngine->hasOutgoingChanges()) { | 4285 | if (_physicsEngine->hasOutgoingChanges()) { |
| 4120 | getEntities()->getTree()->withWriteLock([&] { | 4286 | getEntities()->getTree()->withWriteLock([&] { |
| @@ -4156,20 +4322,20 @@ void Application::update(float deltaTime) { · 35 unmodified lines | |||
| 4156 | _avatarSimCounter.increment(); | 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 | avatarManager->updateOtherAvatars(deltaTime); | 4326 | avatarManager->updateOtherAvatars(deltaTime); |
| 4161 | } | 4327 | } |
| 4162 | 4328 | ||
| 4163 | qApp->updateMyAvatarLookAtPosition(); | 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 | avatarManager->updateMyAvatar(deltaTime); | 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 | PerformanceTimer perfTimer("overlays"); | 4339 | PerformanceTimer perfTimer("overlays"); |
| 4174 | _overlays.update(deltaTime); | 4340 | _overlays.update(deltaTime); |
| 4175 | } | 4341 | } |
| @@ -4189,7 +4355,7 @@ void Application::update(float deltaTime) { · 13 unmodified lines | |||
| 4189 | 4355 | ||
| 4190 | // Update my voxel servers with my current voxel query... | 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 | QMutexLocker viewLocker(&_viewMutex); | 4359 | QMutexLocker viewLocker(&_viewMutex); |
| 4194 | PerformanceTimer perfTimer("queryOctree"); | 4360 | PerformanceTimer perfTimer("queryOctree"); |
| 4195 | quint64 sinceLastQuery = now - _lastQueriedTime; | 4361 | quint64 sinceLastQuery = now - _lastQueriedTime; |
| @@ -4229,7 +4395,7 @@ void Application::update(float deltaTime) { · 33 unmodified lines | |||
| 4229 | avatarManager->postUpdate(deltaTime); | 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 | std::unique_lock<std::mutex> guard(_postUpdateLambdasLock); | 4400 | std::unique_lock<std::mutex> guard(_postUpdateLambdasLock); |
| 4235 | for (auto& iter : _postUpdateLambdas) { | 4401 | for (auto& iter : _postUpdateLambdas) { |
| @@ -4506,8 +4672,6 @@ QRect Application::getDesirableApplicationGeometry() const { · 270 unmodified lines | |||
| 4506 | // or the "myCamera". | 4672 | // or the "myCamera". |
| 4507 | // | 4673 | // |
| 4508 | void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { | 4674 | void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { |
| 4509 | PerformanceTimer perfTimer("loadViewFrustum"); | ||
| 4510 | PROFILE_RANGE(__FUNCTION__); | ||
| 4511 | // We will use these below, from either the camera or head vectors calculated above | 4675 | // We will use these below, from either the camera or head vectors calculated above |
| 4512 | viewFrustum.setProjection(camera.getProjection()); | 4676 | viewFrustum.setProjection(camera.getProjection()); |
| 4513 | 4677 | ||
| @@ -4683,7 +4847,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 169 unmodified lines | |||
| 4683 | myAvatar->preDisplaySide(renderArgs); | 4847 | myAvatar->preDisplaySide(renderArgs); |
| 4684 | 4848 | ||
| 4685 | activeRenderingThread = QThread::currentThread(); | 4849 | activeRenderingThread = QThread::currentThread(); |
| 4686 | PROFILE_RANGE(__FUNCTION__); | 4850 | PROFILE_RANGE(render, __FUNCTION__); |
| 4687 | PerformanceTimer perfTimer("display"); | 4851 | PerformanceTimer perfTimer("display"); |
| 4688 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); | 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 | startUpload(nullptr, nullptr); | 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 | void Application::packageModel() { | 6093 | void Application::packageModel() { |
| 5472 | ModelPackager::package(); | 6094 | ModelPackager::package(); |
| 5473 | } | 6095 | } |