Showing the changes to interface/src/Application.cpp (1 of 846 files). Show all
Unified · Split
| @@ -120,6 +120,7 @@ · 119 unmodified lines | ||
| 120 | 120 | #include <recording/Recorder.h> |
| 121 | 121 | #include <QmlWebWindowClass.h> |
| 122 | 122 | #include <Preferences.h> |
| 123 | +#include <display-plugins/CompositorHelper.h> | |
| 123 | 124 | |
| 124 | 125 | #include "AnimDebugDraw.h" |
| 125 | 126 | #include "AudioClient.h" |
| @@ -140,6 +141,7 @@ · 14 unmodified lines | ||
| 140 | 141 | #include "ModelPackager.h" |
| 141 | 142 | #include "PluginContainerProxy.h" |
| 142 | 143 | #include "scripting/AccountScriptingInterface.h" |
| 144 | +#include "scripting/AssetMappingsScriptingInterface.h" | |
| 143 | 145 | #include "scripting/AudioDeviceScriptingInterface.h" |
| 144 | 146 | #include "scripting/ClipboardScriptingInterface.h" |
| 145 | 147 | #include "scripting/DesktopScriptingInterface.h" |
| @@ -157,7 +159,6 @@ · 11 unmodified lines | ||
| 157 | 159 | #include "Stars.h" |
| 158 | 160 | #include "ui/AddressBarDialog.h" |
| 159 | 161 | #include "ui/AvatarInputs.h" |
| 160 | −#include "ui/AssetUploadDialogFactory.h" | |
| 161 | 162 | #include "ui/DialogsManager.h" |
| 162 | 163 | #include "ui/LoginDialog.h" |
| 163 | 164 | #include "ui/overlays/Cube3DOverlay.h" |
| @@ -194,6 +195,7 @@ static const QString FBX_EXTENSION = ".fbx"; · 30 unmodified lines | ||
| 194 | 195 | static const QString OBJ_EXTENSION = ".obj"; |
| 195 | 196 | static const QString AVA_JSON_EXTENSION = ".ava.json"; |
| 196 | 197 | |
| 198 | +static const int MSECS_PER_SEC = 1000; | |
| 197 | 199 | static const int MIRROR_VIEW_TOP_PADDING = 5; |
| 198 | 200 | static const int MIRROR_VIEW_LEFT_PADDING = 10; |
| 199 | 201 | static const int MIRROR_VIEW_WIDTH = 265; |
| @@ -210,8 +212,6 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; · 10 unmodified lines | ||
| 210 | 212 | |
| 211 | 213 | static const unsigned int THROTTLED_SIM_FRAMERATE = 15; |
| 212 | 214 | static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; |
| 213 | −static const unsigned int CAPPED_SIM_FRAMERATE = 120; | |
| 214 | −static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE; | |
| 215 | 215 | |
| 216 | 216 | static const uint32_t INVALID_FRAME = UINT32_MAX; |
| 217 | 217 | |
| @@ -224,7 +224,6 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda · 6 unmodified lines | ||
| 224 | 224 | static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js"); |
| 225 | 225 | #endif |
| 226 | 226 | |
| 227 | −const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; | |
| 228 | 227 | Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); |
| 229 | 228 | |
| 230 | 229 | const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions { |
| @@ -236,6 +235,107 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi · 5 unmodified lines | ||
| 236 | 235 | { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl } |
| 237 | 236 | }; |
| 238 | 237 | |
| 238 | +class DeadlockWatchdogThread : public QThread { | |
| 239 | +public: | |
| 240 | + static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1; | |
| 241 | + static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1; | |
| 242 | + static const unsigned long HEARTBEAT_REPORT_INTERVAL_USECS = 5 * USECS_PER_SECOND; | |
| 243 | + static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND; | |
| 244 | + static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large | |
| 245 | + static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples | |
| 246 | + | |
| 247 | + // Set the heartbeat on launch | |
| 248 | + DeadlockWatchdogThread() { | |
| 249 | + setObjectName("Deadlock Watchdog"); | |
| 250 | + // Give the heartbeat an initial value | |
| 251 | + _heartbeat = usecTimestampNow(); | |
| 252 | + connect(qApp, &QCoreApplication::aboutToQuit, [this] { | |
| 253 | + _quit = true; | |
| 254 | + }); | |
| 255 | + } | |
| 256 | + | |
| 257 | + void updateHeartbeat() { | |
| 258 | + auto now = usecTimestampNow(); | |
| 259 | + auto elapsed = now - _heartbeat; | |
| 260 | + _movingAverage.addSample(elapsed); | |
| 261 | + _heartbeat = now; | |
| 262 | + } | |
| 263 | + | |
| 264 | + void deadlockDetectionCrash() { | |
| 265 | + uint32_t* crashTrigger = nullptr; | |
| 266 | + *crashTrigger = 0xDEAD10CC; | |
| 267 | + } | |
| 268 | + | |
| 269 | + void run() override { | |
| 270 | + while (!_quit) { | |
| 271 | + QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); | |
| 272 | + | |
| 273 | + uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us | |
| 274 | + uint64_t now = usecTimestampNow(); | |
| 275 | + auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0; | |
| 276 | + auto sinceLastReport = (now > _lastReport) ? now - _lastReport : 0; | |
| 277 | + auto elapsedMovingAverage = _movingAverage.getAverage(); | |
| 278 | + | |
| 279 | + if (elapsedMovingAverage > _maxElapsedAverage) { | |
| 280 | + qDebug() << "DEADLOCK WATCHDOG NEW maxElapsedAverage:" | |
| 281 | + << "lastHeartbeatAge:" << lastHeartbeatAge | |
| 282 | + << "elapsedMovingAverage:" << elapsedMovingAverage | |
| 283 | + << "maxElapsed:" << _maxElapsed | |
| 284 | + << "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage | |
| 285 | + << "NEW maxElapsedAverage:" << elapsedMovingAverage | |
| 286 | + << "samples:" << _movingAverage.getSamples(); | |
| 287 | + _maxElapsedAverage = elapsedMovingAverage; | |
| 288 | + } | |
| 289 | + if (lastHeartbeatAge > _maxElapsed) { | |
| 290 | + qDebug() << "DEADLOCK WATCHDOG NEW maxElapsed:" | |
| 291 | + << "lastHeartbeatAge:" << lastHeartbeatAge | |
| 292 | + << "elapsedMovingAverage:" << elapsedMovingAverage | |
| 293 | + << "PREVIOUS maxElapsed:" << _maxElapsed | |
| 294 | + << "NEW maxElapsed:" << lastHeartbeatAge | |
| 295 | + << "maxElapsedAverage:" << _maxElapsedAverage | |
| 296 | + << "samples:" << _movingAverage.getSamples(); | |
| 297 | + _maxElapsed = lastHeartbeatAge; | |
| 298 | + } | |
| 299 | + if ((sinceLastReport > HEARTBEAT_REPORT_INTERVAL_USECS) || (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT)) { | |
| 300 | + qDebug() << "DEADLOCK WATCHDOG STATUS -- lastHeartbeatAge:" << lastHeartbeatAge | |
| 301 | + << "elapsedMovingAverage:" << elapsedMovingAverage | |
| 302 | + << "maxElapsed:" << _maxElapsed | |
| 303 | + << "maxElapsedAverage:" << _maxElapsedAverage | |
| 304 | + << "samples:" << _movingAverage.getSamples(); | |
| 305 | + _lastReport = now; | |
| 306 | + } | |
| 307 | + | |
| 308 | +#ifdef NDEBUG | |
| 309 | + if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { | |
| 310 | + qDebug() << "DEADLOCK DETECTED -- " | |
| 311 | + << "lastHeartbeatAge:" << lastHeartbeatAge | |
| 312 | + << "[ lastHeartbeat :" << lastHeartbeat | |
| 313 | + << "now:" << now << " ]" | |
| 314 | + << "elapsedMovingAverage:" << elapsedMovingAverage | |
| 315 | + << "maxElapsed:" << _maxElapsed | |
| 316 | + << "maxElapsedAverage:" << _maxElapsedAverage | |
| 317 | + << "samples:" << _movingAverage.getSamples(); | |
| 318 | + deadlockDetectionCrash(); | |
| 319 | + } | |
| 320 | +#endif | |
| 321 | + } | |
| 322 | + } | |
| 323 | + | |
| 324 | + static std::atomic<uint64_t> _heartbeat; | |
| 325 | + static std::atomic<uint64_t> _lastReport; | |
| 326 | + static std::atomic<uint64_t> _maxElapsed; | |
| 327 | + static std::atomic<int> _maxElapsedAverage; | |
| 328 | + static ThreadSafeMovingAverage<int, HEARTBEAT_SAMPLES> _movingAverage; | |
| 329 | + | |
| 330 | + bool _quit { false }; | |
| 331 | +}; | |
| 332 | + | |
| 333 | +std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat; | |
| 334 | +std::atomic<uint64_t> DeadlockWatchdogThread::_lastReport; | |
| 335 | +std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed; | |
| 336 | +std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage; | |
| 337 | +ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage; | |
| 338 | + | |
| 239 | 339 | #ifdef Q_OS_WIN |
| 240 | 340 | class MyNativeEventFilter : public QAbstractNativeEventFilter { |
| 241 | 341 | public: |
| @@ -317,7 +417,7 @@ bool setupEssentials(int& argc, char** argv) { · 75 unmodified lines | ||
| 317 | 417 | |
| 318 | 418 | Setting::preInit(); |
| 319 | 419 | |
| 320 | − CrashHandler::checkForAndHandleCrash(); | |
| 420 | + bool previousSessionCrashed = CrashHandler::checkForAndHandleCrash(); | |
| 321 | 421 | CrashHandler::writeRunningMarkerFiler(); |
| 322 | 422 | qAddPostRoutine(CrashHandler::deleteRunningMarkerFile); |
| 323 | 423 | |
| @@ -372,14 +472,14 @@ bool setupEssentials(int& argc, char** argv) { · 48 unmodified lines | ||
| 372 | 472 | DependencyManager::set<AutoUpdater>(); |
| 373 | 473 | DependencyManager::set<PathUtils>(); |
| 374 | 474 | DependencyManager::set<InterfaceActionFactory>(); |
| 375 | − DependencyManager::set<AssetClient>(); | |
| 376 | 475 | DependencyManager::set<AudioInjectorManager>(); |
| 377 | 476 | DependencyManager::set<MessagesClient>(); |
| 378 | 477 | DependencyManager::set<UserInputMapper>(); |
| 379 | 478 | DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>(); |
| 380 | 479 | DependencyManager::set<InterfaceParentFinder>(); |
| 381 | 480 | DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp); |
| 382 | − return true; | |
| 481 | + DependencyManager::set<CompositorHelper>(); | |
| 482 | + return previousSessionCrashed; | |
| 383 | 483 | } |
| 384 | 484 | |
| 385 | 485 | // FIXME move to header, or better yet, design some kind of UI manager |
| @@ -400,10 +500,13 @@ PluginContainer* _pluginContainer; · 14 unmodified lines | ||
| 400 | 500 | OffscreenGLCanvas* _chromiumShareContext { nullptr }; |
| 401 | 501 | Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); |
| 402 | 502 | |
| 503 | +Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 }; | |
| 504 | + | |
| 403 | 505 | Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : |
| 404 | 506 | QApplication(argc, argv), |
| 405 | − _dependencyManagerIsSetup(setupEssentials(argc, argv)), | |
| 406 | 507 | _window(new MainWindow(desktop())), |
| 508 | + _sessionRunTimer(startupTimer), | |
| 509 | + _previousSessionCrashed(setupEssentials(argc, argv)), | |
| 407 | 510 | _undoStackScriptingInterface(&_undoStack), |
| 408 | 511 | _frameCount(0), |
| 409 | 512 | _fps(60.0f), |
| @@ -425,11 +528,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 15 unmodified lines | ||
| 425 | 528 | _maxOctreePPS(maxOctreePacketsPerSecond.get()), |
| 426 | 529 | _lastFaceTrackerUpdate(0) |
| 427 | 530 | { |
| 428 | − // FIXME this may be excessivly conservative. On the other hand | |
| 531 | + // FIXME this may be excessivly conservative. On the other hand | |
| 429 | 532 | // maybe I'm used to having an 8-core machine |
| 430 | − // Perhaps find the ideal thread count and subtract 2 or 3 | |
| 533 | + // Perhaps find the ideal thread count and subtract 2 or 3 | |
| 431 | 534 | // (main thread, present thread, random OS load) |
| 432 | − // More threads == faster concurrent loads, but also more concurrent | |
| 535 | + // More threads == faster concurrent loads, but also more concurrent | |
| 433 | 536 | // load on the GPU until we can serialize GPU transfers (off the main thread) |
| 434 | 537 | QThreadPool::globalInstance()->setMaxThreadCount(2); |
| 435 | 538 | thread()->setPriority(QThread::HighPriority); |
| @@ -458,6 +561,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 22 unmodified lines | ||
| 458 | 561 | |
| 459 | 562 | auto nodeList = DependencyManager::get<NodeList>(); |
| 460 | 563 | |
| 564 | + // Set up a watchdog thread to intentionally crash the application on deadlocks | |
| 565 | + _deadlockWatchdogThread = new DeadlockWatchdogThread(); | |
| 566 | + _deadlockWatchdogThread->start(); | |
| 567 | + | |
| 461 | 568 | qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion()); |
| 462 | 569 | |
| 463 | 570 | _bookmarks = new Bookmarks(); // Before setting up the menu |
| @@ -490,9 +597,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 26 unmodified lines | ||
| 490 | 597 | audioThread->setObjectName("Audio Thread"); |
| 491 | 598 | |
| 492 | 599 | auto audioIO = DependencyManager::get<AudioClient>(); |
| 600 | + audioIO->setPositionGetter([]{ | |
| 601 | + auto avatarManager = DependencyManager::get<AvatarManager>(); | |
| 602 | + auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; | |
| 603 | + | |
| 604 | + return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO; | |
| 605 | + }); | |
| 606 | + audioIO->setOrientationGetter([]{ | |
| 607 | + auto avatarManager = DependencyManager::get<AvatarManager>(); | |
| 608 | + auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; | |
| 493 | 609 | |
| 494 | − audioIO->setPositionGetter([this]{ return getMyAvatar()->getPositionForAudio(); }); | |
| 495 | − audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); }); | |
| 610 | + return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY; | |
| 611 | + }); | |
| 496 | 612 | |
| 497 | 613 | audioIO->moveToThread(audioThread); |
| 498 | 614 | recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) { |
| @@ -508,7 +624,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 9 unmodified lines | ||
| 508 | 624 | }); |
| 509 | 625 | |
| 510 | 626 | auto& audioScriptingInterface = AudioScriptingInterface::getInstance(); |
| 511 | − | |
| 512 | 627 | connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); |
| 513 | 628 | connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); |
| 514 | 629 | connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); |
| @@ -530,13 +645,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 15 unmodified lines | ||
| 530 | 645 | |
| 531 | 646 | audioThread->start(); |
| 532 | 647 | |
| 533 | − // Setup AssetClient | |
| 534 | − auto assetClient = DependencyManager::get<AssetClient>(); | |
| 535 | − QThread* assetThread = new QThread; | |
| 536 | − assetThread->setObjectName("Asset Thread"); | |
| 537 | − assetClient->moveToThread(assetThread); | |
| 538 | − connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init); | |
| 539 | − assetThread->start(); | |
| 648 | + ResourceManager::init(); | |
| 649 | + // Make sure we don't time out during slow operations at startup | |
| 650 | + updateHeartbeat(); | |
| 540 | 651 | |
| 541 | 652 | // Setup MessagesClient |
| 542 | 653 | auto messagesClient = DependencyManager::get<MessagesClient>(); |
| @@ -549,18 +660,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 6 unmodified lines | ||
| 549 | 660 | const DomainHandler& domainHandler = nodeList->getDomainHandler(); |
| 550 | 661 | |
| 551 | 662 | connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); |
| 552 | − connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); | |
| 663 | + connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); | |
| 553 | 664 | connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); |
| 554 | 665 | connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); |
| 555 | 666 | connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); |
| 556 | − connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); | |
| 557 | 667 | |
| 558 | 668 | // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one |
| 559 | − const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; | |
| 669 | + const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * MSECS_PER_SEC; | |
| 560 | 670 | |
| 561 | 671 | auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>(); |
| 562 | 672 | connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); |
| 563 | − connect(&locationUpdateTimer, &QTimer::timeout, | |
| 673 | + connect(&locationUpdateTimer, &QTimer::timeout, | |
| 564 | 674 | DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); |
| 565 | 675 | locationUpdateTimer.start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); |
| 566 | 676 | |
| @@ -570,6 +680,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 3 unmodified lines | ||
| 570 | 680 | |
| 571 | 681 | connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); |
| 572 | 682 | connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled); |
| 683 | + connect(nodeList.data(), &NodeList::nodeActivated, this, &Application::nodeActivated); | |
| 573 | 684 | connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID); |
| 574 | 685 | connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID); |
| 575 | 686 | connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset); |
| @@ -578,7 +689,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines | ||
| 578 | 689 | // connect to appropriate slots on AccountManager |
| 579 | 690 | AccountManager& accountManager = AccountManager::getInstance(); |
| 580 | 691 | |
| 581 | − const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000; | |
| 692 | + const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * MSECS_PER_SEC; | |
| 582 | 693 | |
| 583 | 694 | connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance); |
| 584 | 695 | balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS); |
| @@ -590,8 +701,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 5 unmodified lines | ||
| 590 | 701 | connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle); |
| 591 | 702 | |
| 592 | 703 | // set the account manager's root URL and trigger a login request if we don't have the access token |
| 704 | + accountManager.setIsAgent(true); | |
| 593 | 705 | accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); |
| 594 | − UserActivityLogger::getInstance().launch(applicationVersion()); | |
| 706 | + | |
| 707 | + // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. | |
| 708 | + // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. | |
| 709 | + UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get()); | |
| 595 | 710 | |
| 596 | 711 | // once the event loop has started, check and signal for an access token |
| 597 | 712 | QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); |
| @@ -604,7 +719,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 6 unmodified lines | ||
| 604 | 719 | |
| 605 | 720 | connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); |
| 606 | 721 | connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); |
| 607 | − | |
| 722 | + | |
| 608 | 723 | // Save avatar location immediately after a teleport. |
| 609 | 724 | connect(getMyAvatar(), &MyAvatar::positionGoneTo, |
| 610 | 725 | DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); |
| @@ -625,7 +740,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 14 unmodified lines | ||
| 625 | 740 | getEntities()->reloadEntityScripts(); |
| 626 | 741 | }, Qt::QueuedConnection); |
| 627 | 742 | |
| 628 | − connect(scriptEngines, &ScriptEngines::scriptLoadError, | |
| 743 | + connect(scriptEngines, &ScriptEngines::scriptLoadError, | |
| 629 | 744 | scriptEngines, [](const QString& filename, const QString& error){ |
| 630 | 745 | OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load."); |
| 631 | 746 | }, Qt::QueuedConnection); |
| @@ -646,16 +761,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 14 unmodified lines | ||
| 646 | 761 | connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); |
| 647 | 762 | identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); |
| 648 | 763 | |
| 649 | − QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); | |
| 650 | − QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); | |
| 651 | − QNetworkDiskCache* cache = new QNetworkDiskCache(); | |
| 652 | − cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); | |
| 653 | − cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); | |
| 654 | − networkAccessManager.setCache(cache); | |
| 655 | − | |
| 656 | 764 | ResourceCache::setRequestLimit(3); |
| 657 | 765 | |
| 658 | 766 | _glWidget = new GLCanvas(); |
| 767 | + getApplicationCompositor().setRenderingWidget(_glWidget); | |
| 659 | 768 | _window->setCentralWidget(_glWidget); |
| 660 | 769 | |
| 661 | 770 | _window->restoreGeometry(); |
| @@ -663,15 +772,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 1 unmodified line | ||
| 663 | 772 | |
| 664 | 773 | _glWidget->setFocusPolicy(Qt::StrongFocus); |
| 665 | 774 | _glWidget->setFocus(); |
| 775 | + | |
| 666 | 776 | #ifdef Q_OS_MAC |
| 667 | − // OSX doesn't seem to provide for hiding the cursor only on the GL widget | |
| 668 | − _window->setCursor(Qt::BlankCursor); | |
| 777 | + auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget | |
| 669 | 778 | #else |
| 670 | − // On windows and linux, hiding the top level cursor also means it's invisible | |
| 671 | − // when hovering over the window menu, which is a pain, so only hide it for | |
| 672 | − // the GL surface | |
| 673 | − _glWidget->setCursor(Qt::BlankCursor); | |
| 779 | + // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the | |
| 780 | + // window menu, which is a pain, so only hide it for the GL surface | |
| 781 | + auto cursorTarget = _glWidget; | |
| 674 | 782 | #endif |
| 783 | + cursorTarget->setCursor(Qt::BlankCursor); | |
| 675 | 784 | |
| 676 | 785 | // enable mouse tracking; otherwise, we only get drag events |
| 677 | 786 | _glWidget->setMouseTracking(true); |
| @@ -687,8 +796,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 9 unmodified lines | ||
| 687 | 796 | _offscreenContext->create(_glWidget->context()->contextHandle()); |
| 688 | 797 | _offscreenContext->makeCurrent(); |
| 689 | 798 | initializeGL(); |
| 690 | − | |
| 691 | 799 | _offscreenContext->makeCurrent(); |
| 800 | + // Make sure we don't time out during slow operations at startup | |
| 801 | + updateHeartbeat(); | |
| 692 | 802 | |
| 693 | 803 | // Tell our entity edit sender about our known jurisdictions |
| 694 | 804 | _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); |
| @@ -699,6 +809,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines | ||
| 699 | 809 | _entityEditSender.setPacketsPerSecond(3000); // super high!! |
| 700 | 810 | |
| 701 | 811 | _overlays.init(); // do this before scripts load |
| 812 | + // Make sure we don't time out during slow operations at startup | |
| 813 | + updateHeartbeat(); | |
| 702 | 814 | |
| 703 | 815 | connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit())); |
| 704 | 816 | |
| @@ -709,7 +821,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines | ||
| 709 | 821 | connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, |
| 710 | 822 | bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); |
| 711 | 823 | |
| 712 | − connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, | |
| 824 | + // FIXME -- I'm a little concerned about this. | |
| 825 | + connect(getMyAvatar()->getSkeletonModel().get(), &SkeletonModel::skeletonLoaded, | |
| 713 | 826 | this, &Application::checkSkeleton, Qt::QueuedConnection); |
| 714 | 827 | |
| 715 | 828 | // Setup the userInputMapper with the actions |
| @@ -786,7 +899,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 70 unmodified lines | ||
| 786 | 899 | } |
| 787 | 900 | |
| 788 | 901 | if (action == controller::toInt(controller::Action::RETICLE_CLICK)) { |
| 789 | − auto reticlePos = _compositor.getReticlePosition(); | |
| 902 | + auto reticlePos = getApplicationCompositor().getReticlePosition(); | |
| 790 | 903 | QPoint globalPos(reticlePos.x, reticlePos.y); |
| 791 | 904 | |
| 792 | 905 | // FIXME - it would be nice if this was self contained in the _compositor or Reticle class |
| @@ -809,14 +922,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 16 unmodified lines | ||
| 809 | 922 | } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) { |
| 810 | 923 | cycleCamera(); |
| 811 | 924 | } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { |
| 812 | − auto reticlePosition = _compositor.getReticlePosition(); | |
| 925 | + auto reticlePosition = getApplicationCompositor().getReticlePosition(); | |
| 813 | 926 | offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y))); |
| 814 | 927 | } else if (action == controller::toInt(controller::Action::RETICLE_X)) { |
| 815 | − auto oldPos = _compositor.getReticlePosition(); | |
| 816 | − _compositor.setReticlePosition({ oldPos.x + state, oldPos.y }); | |
| 928 | + auto oldPos = getApplicationCompositor().getReticlePosition(); | |
| 929 | + getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y }); | |
| 817 | 930 | } else if (action == controller::toInt(controller::Action::RETICLE_Y)) { |
| 818 | − auto oldPos = _compositor.getReticlePosition(); | |
| 819 | − _compositor.setReticlePosition({ oldPos.x, oldPos.y + state }); | |
| 931 | + auto oldPos = getApplicationCompositor().getReticlePosition(); | |
| 932 | + getApplicationCompositor().setReticlePosition({ oldPos.x, oldPos.y + state }); | |
| 933 | + } else if (action == controller::toInt(controller::Action::TOGGLE_OVERLAY)) { | |
| 934 | + toggleOverlays(); | |
| 820 | 935 | } |
| 821 | 936 | } |
| 822 | 937 | }); |
| @@ -825,7 +940,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines | ||
| 825 | 940 | _applicationStateDevice = std::make_shared<controller::StateController>(); |
| 826 | 941 | |
| 827 | 942 | _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float { |
| 828 | − return (float)qApp->getAvatarUpdater()->isHMDMode(); | |
| 943 | + return (float)qApp->isHMDMode(); | |
| 829 | 944 | })); |
| 830 | 945 | _applicationStateDevice->addInputVariant(QString("SnapTurn"), controller::StateController::ReadLambda([]() -> float { |
| 831 | 946 | return (float)qApp->getMyAvatar()->getSnapTurn(); |
| @@ -849,8 +964,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 17 unmodified lines | ||
| 849 | 964 | scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation()); |
| 850 | 965 | // do this as late as possible so that all required subsystems are initialized |
| 851 | 966 | scriptEngines->loadScripts(); |
| 967 | + // Make sure we don't time out during slow operations at startup | |
| 968 | + updateHeartbeat(); | |
| 852 | 969 | |
| 853 | 970 | loadSettings(); |
| 971 | + // Make sure we don't time out during slow operations at startup | |
| 972 | + updateHeartbeat(); | |
| 973 | + | |
| 854 | 974 | int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now |
| 855 | 975 | connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings); |
| 856 | 976 | connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start())); |
| @@ -904,9 +1024,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 47 unmodified lines | ||
| 904 | 1024 | SpacemouseManager::getInstance().init(); |
| 905 | 1025 | #endif |
| 906 | 1026 | |
| 907 | − auto& packetReceiver = nodeList->getPacketReceiver(); | |
| 908 | − packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); | |
| 909 | − | |
| 910 | 1027 | // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it |
| 911 | 1028 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); |
| 912 | 1029 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, |
| @@ -966,8 +1083,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 53 unmodified lines | ||
| 966 | 1083 | } |
| 967 | 1084 | }); |
| 968 | 1085 | |
| 1086 | + // Make sure we don't time out during slow operations at startup | |
| 1087 | + updateHeartbeat(); | |
| 1088 | + | |
| 969 | 1089 | connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); |
| 970 | 1090 | qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); |
| 1091 | + | |
| 971 | 1092 | _idleTimer = new QTimer(this); |
| 972 | 1093 | connect(_idleTimer, &QTimer::timeout, [=] { |
| 973 | 1094 | idle(usecTimestampNow()); |
| @@ -976,14 +1097,49 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines | ||
| 976 | 1097 | disconnect(_idleTimer); |
| 977 | 1098 | }); |
| 978 | 1099 | // Setting the interval to zero forces this to get called whenever there are no messages |
| 979 | − // in the queue, which can be pretty damn frequent. Hence the idle function has a bunch | |
| 1100 | + // in the queue, which can be pretty damn frequent. Hence the idle function has a bunch | |
| 980 | 1101 | // of logic to abort early if it's being called too often. |
| 981 | 1102 | _idleTimer->start(0); |
| 982 | 1103 | } |
| 983 | 1104 | |
| 1105 | + | |
| 1106 | +void Application::checkChangeCursor() { | |
| 1107 | + QMutexLocker locker(&_changeCursorLock); | |
| 1108 | + if (_cursorNeedsChanging) { | |
| 1109 | +#ifdef Q_OS_MAC | |
| 1110 | + auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget | |
| 1111 | +#else | |
| 1112 | + // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the | |
| 1113 | + // window menu, which is a pain, so only hide it for the GL surface | |
| 1114 | + auto cursorTarget = _glWidget; | |
| 1115 | +#endif | |
| 1116 | + cursorTarget->setCursor(_desiredCursor); | |
| 1117 | + | |
| 1118 | + _cursorNeedsChanging = false; | |
| 1119 | + } | |
| 1120 | +} | |
| 1121 | + | |
| 1122 | +void Application::showCursor(const QCursor& cursor) { | |
| 1123 | + QMutexLocker locker(&_changeCursorLock); | |
| 1124 | + _desiredCursor = cursor; | |
| 1125 | + _cursorNeedsChanging = true; | |
| 1126 | +} | |
| 1127 | + | |
| 1128 | +void Application::updateHeartbeat() { | |
| 1129 | + static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat(); | |
| 1130 | +} | |
| 1131 | + | |
| 984 | 1132 | void Application::aboutToQuit() { |
| 985 | 1133 | emit beforeAboutToQuit(); |
| 986 | 1134 | |
| 1135 | + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { | |
| 1136 | + QString name = inputPlugin->getName(); | |
| 1137 | + QAction* action = Menu::getInstance()->getActionForOption(name); | |
| 1138 | + if (action->isChecked()) { | |
| 1139 | + inputPlugin->deactivate(); | |
| 1140 | + } | |
| 1141 | + } | |
| 1142 | + | |
| 987 | 1143 | getActiveDisplayPlugin()->deactivate(); |
| 988 | 1144 | |
| 989 | 1145 | _aboutToQuit = true; |
| @@ -1024,11 +1180,10 @@ void Application::cleanupBeforeQuit() { · 34 unmodified lines | ||
| 1024 | 1180 | getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts |
| 1025 | 1181 | DependencyManager::get<ScriptEngines>()->saveScripts(); |
| 1026 | 1182 | DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts |
| 1027 | − DependencyManager::destroy<ScriptEngines>(); | |
| 1183 | + DependencyManager::destroy<ScriptEngines>(); | |
| 1028 | 1184 | |
| 1029 | 1185 | // first stop all timers directly or by invokeMethod |
| 1030 | 1186 | // depending on what thread they run in |
| 1031 | − _avatarUpdate->terminate(); | |
| 1032 | 1187 | locationUpdateTimer.stop(); |
| 1033 | 1188 | balanceUpdateTimer.stop(); |
| 1034 | 1189 | identityPacketTimer.stop(); |
| @@ -1062,13 +1217,6 @@ void Application::cleanupBeforeQuit() { · 27 unmodified lines | ||
| 1062 | 1217 | DependencyManager::destroy<OffscreenUi>(); |
| 1063 | 1218 | } |
| 1064 | 1219 | |
| 1065 | −void Application::emptyLocalCache() { | |
| 1066 | − if (auto cache = NetworkAccessManager::getInstance().cache()) { | |
| 1067 | − qDebug() << "DiskCacheEditor::clear(): Clearing disk cache."; | |
| 1068 | − cache->clear(); | |
| 1069 | − } | |
| 1070 | −} | |
| 1071 | − | |
| 1072 | 1220 | Application::~Application() { |
| 1073 | 1221 | EntityTreePointer tree = getEntities()->getTree(); |
| 1074 | 1222 | tree->setSimulation(NULL); |
| @@ -1076,20 +1224,10 @@ Application::~Application() { · 1 unmodified line | ||
| 1076 | 1224 | _octreeProcessor.terminate(); |
| 1077 | 1225 | _entityEditSender.terminate(); |
| 1078 | 1226 | |
| 1079 | − Menu::getInstance()->deleteLater(); | |
| 1080 | − | |
| 1081 | 1227 | _physicsEngine->setCharacterController(NULL); |
| 1082 | 1228 | |
| 1083 | 1229 | ModelEntityItem::cleanupLoadedAnimations(); |
| 1084 | 1230 | |
| 1085 | − foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { | |
| 1086 | − QString name = inputPlugin->getName(); | |
| 1087 | − QAction* action = Menu::getInstance()->getActionForOption(name); | |
| 1088 | − if (action->isChecked()) { | |
| 1089 | − inputPlugin->deactivate(); | |
| 1090 | − } | |
| 1091 | − } | |
| 1092 | − | |
| 1093 | 1231 | // remove avatars from physics engine |
| 1094 | 1232 | DependencyManager::get<AvatarManager>()->clearOtherAvatars(); |
| 1095 | 1233 | VectorOfMotionStates motionStates; |
| @@ -1106,11 +1244,7 @@ Application::~Application() { · 10 unmodified lines | ||
| 1106 | 1244 | DependencyManager::destroy<ScriptCache>(); |
| 1107 | 1245 | DependencyManager::destroy<SoundCache>(); |
| 1108 | 1246 | |
| 1109 | − // cleanup the AssetClient thread | |
| 1110 | − QThread* assetThread = DependencyManager::get<AssetClient>()->thread(); | |
| 1111 | − DependencyManager::destroy<AssetClient>(); | |
| 1112 | − assetThread->quit(); | |
| 1113 | − assetThread->wait(); | |
| 1247 | + ResourceManager::cleanup(); | |
| 1114 | 1248 | |
| 1115 | 1249 | QThread* nodeThread = DependencyManager::get<NodeList>()->thread(); |
| 1116 | 1250 | |
| @@ -1126,6 +1260,10 @@ Application::~Application() { · 9 unmodified lines | ||
| 1126 | 1260 | #if 0 |
| 1127 | 1261 | ConnexionClient::getInstance().destroy(); |
| 1128 | 1262 | #endif |
| 1263 | + // The window takes ownership of the menu, so this has the side effect of destroying it. | |
| 1264 | + _window->setMenuBar(nullptr); | |
| 1265 | + | |
| 1266 | + _window->deleteLater(); | |
| 1129 | 1267 | |
| 1130 | 1268 | qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages |
| 1131 | 1269 | } |
| @@ -1199,7 +1337,7 @@ void Application::initializeUi() { · 67 unmodified lines | ||
| 1199 | 1337 | // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to |
| 1200 | 1338 | // support the window management and scripting proxies for VR use |
| 1201 | 1339 | offscreenUi->createDesktop(QString("hifi/Desktop.qml")); |
| 1202 | − | |
| 1340 | + | |
| 1203 | 1341 | // FIXME either expose so that dialogs can set this themselves or |
| 1204 | 1342 | // do better detection in the offscreen UI of what has focus |
| 1205 | 1343 | offscreenUi->setNavigationFocused(false); |
| @@ -1212,10 +1350,10 @@ void Application::initializeUi() { · 6 unmodified lines | ||
| 1212 | 1350 | |
| 1213 | 1351 | setupPreferences(); |
| 1214 | 1352 | |
| 1215 | − // For some reason there is already an "Application" object in the QML context, | |
| 1353 | + // For some reason there is already an "Application" object in the QML context, | |
| 1216 | 1354 | // though I can't find it. Hence, "ApplicationInterface" |
| 1217 | 1355 | rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader()); |
| 1218 | − rootContext->setContextProperty("ApplicationInterface", this); | |
| 1356 | + rootContext->setContextProperty("ApplicationInterface", this); | |
| 1219 | 1357 | rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data()); |
| 1220 | 1358 | rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); |
| 1221 | 1359 | rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data()); |
| @@ -1229,6 +1367,7 @@ void Application::initializeUi() { · 7 unmodified lines | ||
| 1229 | 1367 | rootContext->setContextProperty("Quat", new Quat()); |
| 1230 | 1368 | rootContext->setContextProperty("Vec3", new Vec3()); |
| 1231 | 1369 | rootContext->setContextProperty("Uuid", new ScriptUUID()); |
| 1370 | + rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface()); | |
| 1232 | 1371 | |
| 1233 | 1372 | rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data()); |
| 1234 | 1373 | |
| @@ -1258,15 +1397,17 @@ void Application::initializeUi() { · 23 unmodified lines | ||
| 1258 | 1397 | rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data()); |
| 1259 | 1398 | rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data()); |
| 1260 | 1399 | rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get()); |
| 1261 | − rootContext->setContextProperty("Reticle", _compositor.getReticleInterface()); | |
| 1400 | + rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); | |
| 1401 | + | |
| 1402 | + rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); | |
| 1262 | 1403 | |
| 1263 | 1404 | _glWidget->installEventFilter(offscreenUi.data()); |
| 1264 | 1405 | offscreenUi->setMouseTranslator([=](const QPointF& pt) { |
| 1265 | 1406 | QPointF result = pt; |
| 1266 | 1407 | auto displayPlugin = getActiveDisplayPlugin(); |
| 1267 | 1408 | if (displayPlugin->isHmd()) { |
| 1268 | − _compositor.handleRealMouseMoveEvent(false); | |
| 1269 | − auto resultVec = _compositor.getReticlePosition(); | |
| 1409 | + getApplicationCompositor().handleRealMouseMoveEvent(false); | |
| 1410 | + auto resultVec = getApplicationCompositor().getReticlePosition(); | |
| 1270 | 1411 | result = QPointF(resultVec.x, resultVec.y); |
| 1271 | 1412 | } |
| 1272 | 1413 | return result.toPoint(); |
| @@ -1293,10 +1434,30 @@ void Application::initializeUi() { · 20 unmodified lines | ||
| 1293 | 1434 | _touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin); |
| 1294 | 1435 | } |
| 1295 | 1436 | } |
| 1437 | + _window->setMenuBar(new Menu()); | |
| 1296 | 1438 | updateInputModes(); |
| 1439 | + | |
| 1440 | + auto compositorHelper = DependencyManager::get<CompositorHelper>(); | |
| 1441 | + connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, [=] { | |
| 1442 | + if (isHMDMode()) { | |
| 1443 | + showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor); | |
| 1444 | + } | |
| 1445 | + }); | |
| 1297 | 1446 | } |
| 1298 | 1447 | |
| 1299 | 1448 | void Application::paintGL() { |
| 1449 | + | |
| 1450 | + updateHeartbeat(); | |
| 1451 | + | |
| 1452 | + // Some plugins process message events, potentially leading to | |
| 1453 | + // re-entering a paint event. don't allow further processing if this | |
| 1454 | + // happens | |
| 1455 | + if (_inPaint) { | |
| 1456 | + return; | |
| 1457 | + } | |
| 1458 | + _inPaint = true; | |
| 1459 | + Finally clearFlagLambda([this] { _inPaint = false; }); | |
| 1460 | + | |
| 1300 | 1461 | // paintGL uses a queued connection, so we can get messages from the queue even after we've quit |
| 1301 | 1462 | // and the plugins have shutdown |
| 1302 | 1463 | if (_aboutToQuit) { |
| @@ -1322,33 +1483,26 @@ void Application::paintGL() { · 19 unmodified lines | ||
| 1322 | 1483 | _lastFramesPerSecondUpdate = now; |
| 1323 | 1484 | } |
| 1324 | 1485 | |
| 1325 | − if (_isGLInitialized) { | |
| 1326 | − idle(now); | |
| 1327 | − } | |
| 1328 | − | |
| 1329 | − PROFILE_RANGE(__FUNCTION__); | |
| 1486 | + PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); | |
| 1330 | 1487 | PerformanceTimer perfTimer("paintGL"); |
| 1331 | 1488 | |
| 1332 | 1489 | if (nullptr == _displayPlugin) { |
| 1333 | 1490 | return; |
| 1334 | 1491 | } |
| 1335 | 1492 | |
| 1336 | − // Some plugins process message events, potentially leading to | |
| 1337 | − // re-entering a paint event. don't allow further processing if this | |
| 1338 | − // happens | |
| 1339 | − if (_inPaint) { | |
| 1340 | − return; | |
| 1341 | − } | |
| 1342 | − _inPaint = true; | |
| 1343 | − Finally clearFlagLambda([this] { _inPaint = false; }); | |
| 1344 | − | |
| 1345 | 1493 | auto displayPlugin = getActiveDisplayPlugin(); |
| 1346 | 1494 | // FIXME not needed anymore? |
| 1347 | 1495 | _offscreenContext->makeCurrent(); |
| 1348 | 1496 | |
| 1497 | + displayPlugin->beginFrameRender(_frameCount); | |
| 1498 | + | |
| 1349 | 1499 | // update the avatar with a fresh HMD pose |
| 1350 | 1500 | getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); |
| 1351 | 1501 | |
| 1502 | + // update sensorToWorldMatrix for camera and hand controllers | |
| 1503 | + getMyAvatar()->updateSensorToWorldMatrix(); | |
| 1504 | + | |
| 1505 | + | |
| 1352 | 1506 | auto lodManager = DependencyManager::get<LODManager>(); |
| 1353 | 1507 | |
| 1354 | 1508 | |
| @@ -1390,9 +1544,10 @@ void Application::paintGL() { · 35 unmodified lines | ||
| 1390 | 1544 | QSize size = getDeviceSize(); |
| 1391 | 1545 | renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); |
| 1392 | 1546 | _applicationOverlay.renderOverlay(&renderArgs); |
| 1393 | − gpu::FramebufferPointer overlayFramebuffer = _applicationOverlay.getOverlayFramebuffer(); | |
| 1394 | − | |
| 1395 | − | |
| 1547 | + auto overlayTexture = _applicationOverlay.acquireOverlay(); | |
| 1548 | + if (overlayTexture) { | |
| 1549 | + displayPlugin->submitOverlayTexture(overlayTexture); | |
| 1550 | + } | |
| 1396 | 1551 | } |
| 1397 | 1552 | |
| 1398 | 1553 | glm::vec3 boomOffset; |
| @@ -1402,7 +1557,6 @@ void Application::paintGL() { · 3 unmodified lines | ||
| 1402 | 1557 | auto myAvatar = getMyAvatar(); |
| 1403 | 1558 | boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT; |
| 1404 | 1559 | |
| 1405 | − myAvatar->startCapture(); | |
| 1406 | 1560 | if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { |
| 1407 | 1561 | Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); |
| 1408 | 1562 | Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); |
| @@ -1490,9 +1644,10 @@ void Application::paintGL() { · 81 unmodified lines | ||
| 1490 | 1644 | if (!isHMDMode()) { |
| 1491 | 1645 | _myCamera.update(1.0f / _fps); |
| 1492 | 1646 | } |
| 1493 | − myAvatar->endCapture(); | |
| 1494 | 1647 | } |
| 1495 | 1648 | |
| 1649 | + getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); | |
| 1650 | + | |
| 1496 | 1651 | // Primary rendering pass |
| 1497 | 1652 | auto framebufferCache = DependencyManager::get<FramebufferCache>(); |
| 1498 | 1653 | const QSize size = framebufferCache->getFrameBufferSize(); |
| @@ -1523,12 +1678,7 @@ void Application::paintGL() { · 24 unmodified lines | ||
| 1523 | 1678 | auto baseProjection = renderArgs._viewFrustum->getProjection(); |
| 1524 | 1679 | auto hmdInterface = DependencyManager::get<HMDScriptingInterface>(); |
| 1525 | 1680 | float IPDScale = hmdInterface->getIPDScale(); |
| 1526 | − | |
| 1527 | − // Tell the plugin what pose we're using to render. In this case we're just using the | |
| 1528 | − // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it | |
| 1529 | − // for rotational timewarp. If we move to support positonal timewarp, we need to | |
| 1530 | − // ensure this contains the full pose composed with the eye offsets. | |
| 1531 | − mat4 headPose = displayPlugin->getHeadPose(_frameCount); | |
| 1681 | + mat4 headPose = displayPlugin->getHeadPose(); | |
| 1532 | 1682 | |
| 1533 | 1683 | // FIXME we probably don't need to set the projection matrix every frame, |
| 1534 | 1684 | // only when the display plugin changes (or in non-HMD modes when the user |
| @@ -1545,7 +1695,11 @@ void Application::paintGL() { · 10 unmodified lines | ||
| 1545 | 1695 | mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); |
| 1546 | 1696 | eyeOffsets[eye] = eyeOffsetTransform; |
| 1547 | 1697 | |
| 1548 | − displayPlugin->setEyeRenderPose(_frameCount, eye, headPose); | |
| 1698 | + // Tell the plugin what pose we're using to render. In this case we're just using the | |
| 1699 | + // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it | |
| 1700 | + // for rotational timewarp. If we move to support positonal timewarp, we need to | |
| 1701 | + // ensure this contains the full pose composed with the eye offsets. | |
| 1702 | + displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform)); | |
| 1549 | 1703 | |
| 1550 | 1704 | eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); |
| 1551 | 1705 | }); |
| @@ -1559,44 +1713,12 @@ void Application::paintGL() { · 7 unmodified lines | ||
| 1559 | 1713 | renderArgs._context->enableStereo(false); |
| 1560 | 1714 | } |
| 1561 | 1715 | |
| 1562 | − // Overlay Composition, needs to occur after screen space effects have completed | |
| 1563 | − // FIXME migrate composition into the display plugins | |
| 1564 | − { | |
| 1565 | − PROFILE_RANGE(__FUNCTION__ "/compositor"); | |
| 1566 | − PerformanceTimer perfTimer("compositor"); | |
| 1567 | − | |
| 1568 | − auto primaryFbo = finalFramebuffer; | |
| 1569 | − | |
| 1570 | − glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo)); | |
| 1571 | − if (displayPlugin->isStereo()) { | |
| 1572 | − QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height())); | |
| 1573 | − glClear(GL_DEPTH_BUFFER_BIT); | |
| 1574 | − for_each_eye([&](Eye eye) { | |
| 1575 | − renderArgs._viewport = toGlm(currentViewport); | |
| 1576 | − if (displayPlugin->isHmd()) { | |
| 1577 | − _compositor.displayOverlayTextureHmd(&renderArgs, eye); | |
| 1578 | − } else { | |
| 1579 | − _compositor.displayOverlayTexture(&renderArgs); | |
| 1580 | − } | |
| 1581 | − }, [&] { | |
| 1582 | − currentViewport.moveLeft(currentViewport.width()); | |
| 1583 | − }); | |
| 1584 | − } else { | |
| 1585 | − glViewport(0, 0, size.width(), size.height()); | |
| 1586 | − _compositor.displayOverlayTexture(&renderArgs); | |
| 1587 | − } | |
| 1588 | − } | |
| 1589 | − | |
| 1590 | 1716 | // deliver final composited scene to the display plugin |
| 1591 | 1717 | { |
| 1592 | 1718 | PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); |
| 1593 | 1719 | PerformanceTimer perfTimer("pluginOutput"); |
| 1594 | 1720 | |
| 1595 | − auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0); | |
| 1596 | − | |
| 1597 | − GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer); | |
| 1598 | − Q_ASSERT(0 != finalTexture); | |
| 1599 | − | |
| 1721 | + auto finalTexture = finalFramebuffer->getRenderBuffer(0); | |
| 1600 | 1722 | Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture)); |
| 1601 | 1723 | _lockedFramebufferMap[finalTexture] = finalFramebuffer; |
| 1602 | 1724 | |
| @@ -1604,10 +1726,9 @@ void Application::paintGL() { · 1 unmodified line | ||
| 1604 | 1726 | { |
| 1605 | 1727 | PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene"); |
| 1606 | 1728 | PerformanceTimer perfTimer("pluginSubmitScene"); |
| 1607 | − displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size)); | |
| 1729 | + displayPlugin->submitSceneTexture(_frameCount, finalTexture); | |
| 1608 | 1730 | } |
| 1609 | 1731 | Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); |
| 1610 | − | |
| 1611 | 1732 | } |
| 1612 | 1733 | |
| 1613 | 1734 | { |
| @@ -1620,7 +1741,6 @@ void Application::paintGL() { · 6 unmodified lines | ||
| 1620 | 1741 | } |
| 1621 | 1742 | |
| 1622 | 1743 | _lastInstantaneousFps = instantaneousFps; |
| 1623 | − _pendingPaint = false; | |
| 1624 | 1744 | } |
| 1625 | 1745 | |
| 1626 | 1746 | void Application::runTests() { |
| @@ -1704,6 +1824,11 @@ bool Application::importSVOFromURL(const QString& urlString) { · 77 unmodified lines | ||
| 1704 | 1824 | } |
| 1705 | 1825 | |
| 1706 | 1826 | bool Application::event(QEvent* event) { |
| 1827 | + | |
| 1828 | + if (!Menu::getInstance()) { | |
| 1829 | + return false; | |
| 1830 | + } | |
| 1831 | + | |
| 1707 | 1832 | if ((int)event->type() == (int)Lambda) { |
| 1708 | 1833 | ((LambdaEvent*)event)->call(); |
| 1709 | 1834 | return true; |
| @@ -1808,7 +1933,7 @@ bool Application::event(QEvent* event) { · 98 unmodified lines | ||
| 1808 | 1933 | bool Application::eventFilter(QObject* object, QEvent* event) { |
| 1809 | 1934 | |
| 1810 | 1935 | if (event->type() == QEvent::Leave) { |
| 1811 | − _compositor.handleLeaveEvent(); | |
| 1936 | + getApplicationCompositor().handleLeaveEvent(); | |
| 1812 | 1937 | } |
| 1813 | 1938 | |
| 1814 | 1939 | if (event->type() == QEvent::ShortcutOverride) { |
| @@ -1862,6 +1987,28 @@ void Application::keyPressEvent(QKeyEvent* event) { · 47 unmodified lines | ||
| 1862 | 1987 | } |
| 1863 | 1988 | break; |
| 1864 | 1989 | |
| 1990 | + case Qt::Key_1: | |
| 1991 | + case Qt::Key_2: | |
| 1992 | + case Qt::Key_3: | |
| 1993 | + case Qt::Key_4: | |
| 1994 | + case Qt::Key_5: | |
| 1995 | + case Qt::Key_6: | |
| 1996 | + case Qt::Key_7: | |
| 1997 | + if (isMeta || isOption) { | |
| 1998 | + unsigned int index = static_cast<unsigned int>(event->key() - Qt::Key_1); | |
| 1999 | + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); | |
| 2000 | + if (index < displayPlugins.size()) { | |
| 2001 | + auto targetPlugin = displayPlugins.at(index); | |
| 2002 | + QString targetName = targetPlugin->getName(); | |
| 2003 | + auto menu = Menu::getInstance(); | |
| 2004 | + QAction* action = menu->getActionForOption(targetName); | |
| 2005 | + if (action && !action->isChecked()) { | |
| 2006 | + action->trigger(); | |
| 2007 | + } | |
| 2008 | + } | |
| 2009 | + } | |
| 2010 | + break; | |
| 2011 | + | |
| 1865 | 2012 | case Qt::Key_X: |
| 1866 | 2013 | if (isShifted && isMeta) { |
| 1867 | 2014 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| @@ -1871,6 +2018,12 @@ void Application::keyPressEvent(QKeyEvent* event) { · 3 unmodified lines | ||
| 1871 | 2018 | } |
| 1872 | 2019 | break; |
| 1873 | 2020 | |
| 2021 | + case Qt::Key_Y: | |
| 2022 | + if (isShifted && isMeta) { | |
| 2023 | + getActiveDisplayPlugin()->cycleDebugOutput(); | |
| 2024 | + } | |
| 2025 | + break; | |
| 2026 | + | |
| 1874 | 2027 | case Qt::Key_B: |
| 1875 | 2028 | if (isMeta) { |
| 1876 | 2029 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| @@ -2022,21 +2175,22 @@ void Application::keyPressEvent(QKeyEvent* event) { · 145 unmodified lines | ||
| 2022 | 2175 | if (isShifted) { |
| 2023 | 2176 | Menu::getInstance()->triggerOption(MenuOption::MiniMirror); |
| 2024 | 2177 | } else { |
| 2025 | − Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)); | |
| 2026 | − if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { | |
| 2178 | + bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); | |
| 2179 | + Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked); | |
| 2180 | + if (isMirrorChecked) { | |
| 2027 | 2181 | Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); |
| 2028 | 2182 | } |
| 2029 | 2183 | cameraMenuChanged(); |
| 2030 | 2184 | } |
| 2031 | 2185 | break; |
| 2032 | − case Qt::Key_P: | |
| 2033 | − Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)); | |
| 2034 | − Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)); | |
| 2035 | − cameraMenuChanged(); | |
| 2036 | − break; | |
| 2037 | − case Qt::Key_O: | |
| 2038 | − _overlayConductor.setEnabled(!_overlayConductor.getEnabled()); | |
| 2186 | + case Qt::Key_P: { | |
| 2187 | + bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson); | |
| 2188 | + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !isFirstPersonChecked); | |
| 2189 | + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, isFirstPersonChecked); | |
| 2190 | + cameraMenuChanged(); | |
| 2039 | 2191 | break; |
| 2192 | + } | |
| 2193 | + | |
| 2040 | 2194 | case Qt::Key_Slash: |
| 2041 | 2195 | Menu::getInstance()->triggerOption(MenuOption::Stats); |
| 2042 | 2196 | break; |
| @@ -2098,7 +2252,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 55 unmodified lines | ||
| 2098 | 2252 | void Application::keyReleaseEvent(QKeyEvent* event) { |
| 2099 | 2253 | if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) { |
| 2100 | 2254 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 2101 | − auto reticlePosition = _compositor.getReticlePosition(); | |
| 2255 | + auto reticlePosition = getApplicationCompositor().getReticlePosition(); | |
| 2102 | 2256 | offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y))); |
| 2103 | 2257 | } |
| 2104 | 2258 | |
| @@ -2187,13 +2341,6 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) { · 82 unmodified lines | ||
| 2187 | 2341 | #endif |
| 2188 | 2342 | } |
| 2189 | 2343 | |
| 2190 | −/// called by ApplicationCompositor when in HMD mode and we're faking our mouse movement | |
| 2191 | −void Application::fakeMouseEvent(QMouseEvent* event) { | |
| 2192 | − _fakedMouseEvent = true; | |
| 2193 | − sendEvent(_glWidget, event); | |
| 2194 | − _fakedMouseEvent = false; | |
| 2195 | −} | |
| 2196 | − | |
| 2197 | 2344 | void Application::mouseMoveEvent(QMouseEvent* event) { |
| 2198 | 2345 | PROFILE_RANGE(__FUNCTION__); |
| 2199 | 2346 | |
| @@ -2203,17 +2350,16 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 3 unmodified lines | ||
| 2203 | 2350 | |
| 2204 | 2351 | maybeToggleMenuVisible(event); |
| 2205 | 2352 | |
| 2353 | + auto& compositor = getApplicationCompositor(); | |
| 2206 | 2354 | // if this is a real mouse event, and we're in HMD mode, then we should use it to move the |
| 2207 | 2355 | // compositor reticle |
| 2208 | − if (!_fakedMouseEvent) { | |
| 2209 | − // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further | |
| 2210 | − if (_compositor.handleRealMouseMoveEvent()) { | |
| 2211 | − return; // bail | |
| 2212 | − } | |
| 2356 | + // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further | |
| 2357 | + if (!compositor.fakeEventActive() && compositor.handleRealMouseMoveEvent()) { | |
| 2358 | + return; // bail | |
| 2213 | 2359 | } |
| 2214 | 2360 | |
| 2215 | 2361 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 2216 | − auto eventPosition = _compositor.getMouseEventPosition(event); | |
| 2362 | + auto eventPosition = compositor.getMouseEventPosition(event); | |
| 2217 | 2363 | QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); |
| 2218 | 2364 | auto button = event->button(); |
| 2219 | 2365 | auto buttons = event->buttons(); |
| @@ -2255,7 +2401,7 @@ void Application::mousePressEvent(QMouseEvent* event) { · 35 unmodified lines | ||
| 2255 | 2401 | // will consume all keyboard events. |
| 2256 | 2402 | offscreenUi->unfocusWindows(); |
| 2257 | 2403 | |
| 2258 | − auto eventPosition = _compositor.getMouseEventPosition(event); | |
| 2404 | + auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); | |
| 2259 | 2405 | QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); |
| 2260 | 2406 | QMouseEvent mappedEvent(event->type(), |
| 2261 | 2407 | transformedPos, |
| @@ -2301,7 +2447,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { · 39 unmodified lines | ||
| 2301 | 2447 | void Application::mouseReleaseEvent(QMouseEvent* event) { |
| 2302 | 2448 | |
| 2303 | 2449 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 2304 | − auto eventPosition = _compositor.getMouseEventPosition(event); | |
| 2450 | + auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); | |
| 2305 | 2451 | QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); |
| 2306 | 2452 | QMouseEvent mappedEvent(event->type(), |
| 2307 | 2453 | transformedPos, |
| @@ -2451,10 +2597,23 @@ bool Application::acceptSnapshot(const QString& urlString) { · 143 unmodified lines | ||
| 2451 | 2597 | static uint32_t _renderedFrameIndex { INVALID_FRAME }; |
| 2452 | 2598 | |
| 2453 | 2599 | void Application::idle(uint64_t now) { |
| 2454 | − if (_aboutToQuit) { | |
| 2600 | + | |
| 2601 | + updateHeartbeat(); | |
| 2602 | + | |
| 2603 | + if (_aboutToQuit || _inPaint) { | |
| 2455 | 2604 | return; // bail early, nothing to do here. |
| 2456 | 2605 | } |
| 2457 | − | |
| 2606 | + | |
| 2607 | + // These tasks need to be done on our first idle, because we don't want the showing of | |
| 2608 | + // overlay subwindows to do a showDesktop() until after the first time through | |
| 2609 | + static bool firstIdle = true; | |
| 2610 | + if (firstIdle) { | |
| 2611 | + firstIdle = false; | |
| 2612 | + auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 2613 | + connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); | |
| 2614 | + _overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays)); | |
| 2615 | + } | |
| 2616 | + | |
| 2458 | 2617 | auto displayPlugin = getActiveDisplayPlugin(); |
| 2459 | 2618 | // depending on whether we're throttling or not. |
| 2460 | 2619 | // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in |
| @@ -2475,38 +2634,30 @@ void Application::idle(uint64_t now) { · 14 unmodified lines | ||
| 2475 | 2634 | _renderedFrameIndex = INVALID_FRAME; |
| 2476 | 2635 | } |
| 2477 | 2636 | |
| 2478 | − // Nested ifs are for clarity in the logic. Don't collapse them into a giant single if. | |
| 2479 | − // Don't saturate the main thread with rendering, no paint calls until the last one is complete | |
| 2480 | − if (!_pendingPaint) { | |
| 2481 | − // Also no paint calls until the display plugin has increased by at least one frame | |
| 2482 | − // (don't render at 90fps if the display plugin only goes at 60) | |
| 2483 | − if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) { | |
| 2484 | − // Record what present frame we're on | |
| 2485 | − _renderedFrameIndex = presentCount; | |
| 2486 | − // Don't allow paint requests to stack up in the event queue | |
| 2487 | − _pendingPaint = true; | |
| 2488 | − // But when we DO request a paint, get to it as soon as possible: high priority | |
| 2489 | − postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority); | |
| 2490 | − } | |
| 2491 | − } | |
| 2492 | − | |
| 2493 | − // For the rest of idle, we want to cap at the max sim rate, so we might not call | |
| 2494 | − // the remaining idle work every paint frame, or vice versa | |
| 2495 | − // In theory this means we could call idle processing more often than painting, | |
| 2496 | − // but in practice, when the paintGL calls aren't keeping up, there's no room left | |
| 2497 | − // in the main thread to call idle more often than paint. | |
| 2498 | − // This check is mostly to keep idle from burning up CPU cycles by running at | |
| 2499 | − // hundreds of idles per second when the rendering is that fast | |
| 2500 | − if ((timeSinceLastUpdateUs / USECS_PER_MSEC) < CAPPED_SIM_FRAME_PERIOD_MS) { | |
| 2501 | − // No paint this round, but might be time for a new idle, otherwise return | |
| 2637 | + // Don't saturate the main thread with rendering and simulation, | |
| 2638 | + // unless display plugin has increased by at least one frame | |
| 2639 | + if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) { | |
| 2640 | + // Record what present frame we're on | |
| 2641 | + _renderedFrameIndex = presentCount; | |
| 2642 | + | |
| 2643 | + // request a paint, get to it as soon as possible: high priority | |
| 2644 | + postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority); | |
| 2645 | + } else { | |
| 2646 | + // there's no use in simulating or rendering faster then the present rate. | |
| 2502 | 2647 | return; |
| 2503 | 2648 | } |
| 2504 | 2649 | |
| 2650 | + PROFILE_RANGE(__FUNCTION__); | |
| 2651 | + | |
| 2505 | 2652 | // We're going to execute idle processing, so restart the last idle timer |
| 2506 | 2653 | _lastTimeUpdated.start(); |
| 2507 | 2654 | |
| 2655 | + checkChangeCursor(); | |
| 2656 | + | |
| 2657 | + Stats::getInstance()->updateStats(); | |
| 2658 | + AvatarInputs::getInstance()->update(); | |
| 2659 | + | |
| 2508 | 2660 | { |
| 2509 | − PROFILE_RANGE(__FUNCTION__); | |
| 2510 | 2661 | static uint64_t lastIdleStart{ now }; |
| 2511 | 2662 | uint64_t idleStartToStartDuration = now - lastIdleStart; |
| 2512 | 2663 | if (idleStartToStartDuration != 0) { |
| @@ -2596,7 +2747,7 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { · 83 unmodified lines | ||
| 2596 | 2747 | } |
| 2597 | 2748 | |
| 2598 | 2749 | ivec2 Application::getMouse() { |
| 2599 | − auto reticlePosition = _compositor.getReticlePosition(); | |
| 2750 | + auto reticlePosition = getApplicationCompositor().getReticlePosition(); | |
| 2600 | 2751 | |
| 2601 | 2752 | // in the HMD, the reticlePosition is the mouse position |
| 2602 | 2753 | if (isHMDMode()) { |
| @@ -2681,43 +2832,50 @@ void Application::calibrateEyeTracker5Points() { · 78 unmodified lines | ||
| 2681 | 2832 | } |
| 2682 | 2833 | #endif |
| 2683 | 2834 | |
| 2684 | −bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) { | |
| 2685 | − QVector<EntityItemPointer> entities; | |
| 2835 | +bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset) { | |
| 2836 | + QHash<EntityItemID, EntityItemPointer> entities; | |
| 2686 | 2837 | |
| 2687 | 2838 | auto entityTree = getEntities()->getTree(); |
| 2688 | 2839 | auto exportTree = std::make_shared<EntityTree>(); |
| 2689 | 2840 | exportTree->createRootElement(); |
| 2690 | 2841 | |
| 2691 | 2842 | glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE); |
| 2692 | − for (auto entityID : entityIDs) { | |
| 2843 | + for (auto entityID : entityIDs) { // Gather entities and properties. | |
| 2693 | 2844 | auto entityItem = entityTree->findEntityByEntityItemID(entityID); |
| 2694 | 2845 | if (!entityItem) { |
| 2846 | + qCWarning(interfaceapp) << "Skipping export of" << entityID << "that is not in scene."; | |
| 2695 | 2847 | continue; |
| 2696 | 2848 | } |
| 2697 | 2849 | |
| 2698 | − auto properties = entityItem->getProperties(); | |
| 2699 | − auto position = properties.getPosition(); | |
| 2700 | − | |
| 2701 | − root.x = glm::min(root.x, position.x); | |
| 2702 | − root.y = glm::min(root.y, position.y); | |
| 2703 | − root.z = glm::min(root.z, position.z); | |
| 2704 | − | |
| 2705 | − entities << entityItem; | |
| 2850 | + if (!givenOffset) { | |
| 2851 | + EntityItemID parentID = entityItem->getParentID(); | |
| 2852 | + if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) { | |
| 2853 | + auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties. | |
| 2854 | + root.x = glm::min(root.x, position.x); | |
| 2855 | + root.y = glm::min(root.y, position.y); | |
| 2856 | + root.z = glm::min(root.z, position.z); | |
| 2857 | + } | |
| 2858 | + } | |
| 2859 | + entities[entityID] = entityItem; | |
| 2706 | 2860 | } |
| 2707 | 2861 | |
| 2708 | 2862 | if (entities.size() == 0) { |
| 2709 | 2863 | return false; |
| 2710 | 2864 | } |
| 2711 | 2865 | |
| 2712 | − for (auto entityItem : entities) { | |
| 2713 | − auto properties = entityItem->getProperties(); | |
| 2714 | − | |
| 2715 | − properties.setPosition(properties.getPosition() - root); | |
| 2716 | − exportTree->addEntity(entityItem->getEntityItemID(), properties); | |
| 2866 | + if (givenOffset) { | |
| 2867 | + root = *givenOffset; | |
| 2868 | + } | |
| 2869 | + for (EntityItemPointer& entityDatum : entities) { | |
| 2870 | + auto properties = entityDatum->getProperties(); | |
| 2871 | + EntityItemID parentID = properties.getParentID(); | |
| 2872 | + if (parentID.isInvalidID()) { | |
| 2873 | + properties.setPosition(properties.getPosition() - root); | |
| 2874 | + } else if (!entities.contains(parentID)) { | |
| 2875 | + entityDatum->globalizeProperties(properties, "Parent %3 of %2 %1 is not selected for export.", -root); | |
| 2876 | + } // else valid parent -- don't offset | |
| 2877 | + exportTree->addEntity(entityDatum->getEntityItemID(), properties); | |
| 2717 | 2878 | } |
| 2718 | − | |
| 2719 | − // remap IDs on export so that we aren't publishing the IDs of entities in our domain | |
| 2720 | − exportTree->remapIDs(); | |
| 2721 | 2879 | |
| 2722 | 2880 | exportTree->writeToJSONFile(filename.toLocal8Bit().constData()); |
| 2723 | 2881 | |
| @@ -2727,37 +2885,19 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt · 3 unmodified lines | ||
| 2727 | 2885 | } |
| 2728 | 2886 | |
| 2729 | 2887 | bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) { |
| 2888 | + glm::vec3 offset(x, y, z); | |
| 2730 | 2889 | QVector<EntityItemPointer> entities; |
| 2731 | − getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities); | |
| 2732 | − | |
| 2733 | − if (entities.size() > 0) { | |
| 2734 | − glm::vec3 root(x, y, z); | |
| 2735 | − auto exportTree = std::make_shared<EntityTree>(); | |
| 2736 | − exportTree->createRootElement(); | |
| 2737 | − | |
| 2738 | − for (int i = 0; i < entities.size(); i++) { | |
| 2739 | − EntityItemProperties properties = entities.at(i)->getProperties(); | |
| 2740 | − EntityItemID id = entities.at(i)->getEntityItemID(); | |
| 2741 | − properties.setPosition(properties.getPosition() - root); | |
| 2742 | − exportTree->addEntity(id, properties); | |
| 2743 | − } | |
| 2744 | − | |
| 2745 | − // remap IDs on export so that we aren't publishing the IDs of entities in our domain | |
| 2746 | − exportTree->remapIDs(); | |
| 2747 | − | |
| 2748 | − exportTree->writeToSVOFile(filename.toLocal8Bit().constData()); | |
| 2749 | − } else { | |
| 2750 | − qCDebug(interfaceapp) << "No models were selected"; | |
| 2751 | − return false; | |
| 2890 | + QVector<EntityItemID> ids; | |
| 2891 | + getEntities()->getTree()->findEntities(AACube(offset, scale), entities); | |
| 2892 | + foreach(EntityItemPointer entity, entities) { | |
| 2893 | + ids << entity->getEntityItemID(); | |
| 2752 | 2894 | } |
| 2753 | − | |
| 2754 | − // restore the main window's active state | |
| 2755 | − _window->activateWindow(); | |
| 2756 | − return true; | |
| 2895 | + return exportEntities(filename, ids, &offset); | |
| 2757 | 2896 | } |
| 2758 | 2897 | |
| 2759 | 2898 | void Application::loadSettings() { |
| 2760 | 2899 | |
| 2900 | + sessionRunTime.set(0); // Just clean living. We're about to saveSettings, which will update value. | |
| 2761 | 2901 | DependencyManager::get<AudioClient>()->loadSettings(); |
| 2762 | 2902 | DependencyManager::get<LODManager>()->loadSettings(); |
| 2763 | 2903 | |
| @@ -2771,6 +2911,7 @@ void Application::loadSettings() { · 7 unmodified lines | ||
| 2771 | 2911 | } |
| 2772 | 2912 | |
| 2773 | 2913 | void Application::saveSettings() { |
| 2914 | + sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC); | |
| 2774 | 2915 | DependencyManager::get<AudioClient>()->saveSettings(); |
| 2775 | 2916 | DependencyManager::get<LODManager>()->saveSettings(); |
| 2776 | 2917 | |
| @@ -2782,16 +2923,8 @@ void Application::saveSettings() { · 5 unmodified lines | ||
| 2782 | 2923 | bool Application::importEntities(const QString& urlOrFilename) { |
| 2783 | 2924 | _entityClipboard->eraseAllOctreeElements(); |
| 2784 | 2925 | |
| 2785 | − QUrl url(urlOrFilename); | |
| 2786 | − | |
| 2787 | − // if the URL appears to be invalid or relative, then it is probably a local file | |
| 2788 | − if (!url.isValid() || url.isRelative()) { | |
| 2789 | − url = QUrl::fromLocalFile(urlOrFilename); | |
| 2790 | − } | |
| 2791 | − | |
| 2792 | − bool success = _entityClipboard->readFromURL(url.toString()); | |
| 2926 | + bool success = _entityClipboard->readFromURL(urlOrFilename); | |
| 2793 | 2927 | if (success) { |
| 2794 | − _entityClipboard->remapIDs(); | |
| 2795 | 2928 | _entityClipboard->reaverageOctreeElements(); |
| 2796 | 2929 | } |
| 2797 | 2930 | return success; |
| @@ -2862,37 +2995,6 @@ void Application::init() { · 64 unmodified lines | ||
| 2862 | 2995 | // Make sure any new sounds are loaded as soon as know about them. |
| 2863 | 2996 | connect(tree.get(), &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound); |
| 2864 | 2997 | connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound); |
| 2865 | − | |
| 2866 | − setAvatarUpdateThreading(); | |
| 2867 | −} | |
| 2868 | − | |
| 2869 | −const bool ENABLE_AVATAR_UPDATE_THREADING = false; | |
| 2870 | −void Application::setAvatarUpdateThreading() { | |
| 2871 | − setAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING); | |
| 2872 | −} | |
| 2873 | −void Application::setRawAvatarUpdateThreading() { | |
| 2874 | − setRawAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING); | |
| 2875 | −} | |
| 2876 | −void Application::setRawAvatarUpdateThreading(bool isThreaded) { | |
| 2877 | − if (_avatarUpdate) { | |
| 2878 | − if (_avatarUpdate->isThreaded() == isThreaded) { | |
| 2879 | − return; | |
| 2880 | − } | |
| 2881 | − _avatarUpdate->terminate(); | |
| 2882 | − } | |
| 2883 | − _avatarUpdate = new AvatarUpdate(); | |
| 2884 | − _avatarUpdate->initialize(isThreaded); | |
| 2885 | −} | |
| 2886 | −void Application::setAvatarUpdateThreading(bool isThreaded) { | |
| 2887 | − if (_avatarUpdate && (_avatarUpdate->isThreaded() == isThreaded)) { | |
| 2888 | − return; | |
| 2889 | − } | |
| 2890 | − | |
| 2891 | − if (_avatarUpdate) { | |
| 2892 | − _avatarUpdate->terminate(); // Must be before we shutdown anim graph. | |
| 2893 | − } | |
| 2894 | − _avatarUpdate = new AvatarUpdate(); | |
| 2895 | − _avatarUpdate->initialize(isThreaded); | |
| 2896 | 2998 | } |
| 2897 | 2999 | |
| 2898 | 3000 | void Application::updateLOD() { |
| @@ -2905,6 +3007,11 @@ void Application::updateLOD() { · 6 unmodified lines | ||
| 2905 | 3007 | } |
| 2906 | 3008 | } |
| 2907 | 3009 | |
| 3010 | +void Application::pushPreRenderLambda(void* key, std::function<void()> func) { | |
| 3011 | + std::unique_lock<std::mutex> guard(_preRenderLambdasLock); | |
| 3012 | + _preRenderLambdas[key] = func; | |
| 3013 | +} | |
| 3014 | + | |
| 2908 | 3015 | // Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone. |
| 2909 | 3016 | // (Maybe this code should be moved there?) |
| 2910 | 3017 | // The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition(). |
| @@ -2920,7 +3027,7 @@ void Application::updateMyAvatarLookAtPosition() { · 9 unmodified lines | ||
| 2920 | 3027 | auto eyeTracker = DependencyManager::get<EyeTracker>(); |
| 2921 | 3028 | |
| 2922 | 3029 | bool isLookingAtSomeone = false; |
| 2923 | − bool isHMD = _avatarUpdate->isHMDMode(); | |
| 3030 | + bool isHMD = qApp->isHMDMode(); | |
| 2924 | 3031 | glm::vec3 lookAtSpot; |
| 2925 | 3032 | if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) { |
| 2926 | 3033 | // Look at the point that the user is looking at. |
| @@ -2929,7 +3036,7 @@ void Application::updateMyAvatarLookAtPosition() { · 2 unmodified lines | ||
| 2929 | 3036 | lookAtPosition.x = -lookAtPosition.x; |
| 2930 | 3037 | } |
| 2931 | 3038 | if (isHMD) { |
| 2932 | − glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount); | |
| 3039 | + glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); | |
| 2933 | 3040 | glm::quat hmdRotation = glm::quat_cast(headPose); |
| 2934 | 3041 | lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition); |
| 2935 | 3042 | } else { |
| @@ -2971,9 +3078,9 @@ void Application::updateMyAvatarLookAtPosition() { · 35 unmodified lines | ||
| 2971 | 3078 | } else { |
| 2972 | 3079 | // I am not looking at anyone else, so just look forward |
| 2973 | 3080 | if (isHMD) { |
| 2974 | − glm::mat4 headPose = _avatarUpdate->getHeadPose() ; | |
| 3081 | + glm::mat4 headPose = myAvatar->getHMDSensorMatrix(); | |
| 2975 | 3082 | glm::quat headRotation = glm::quat_cast(headPose); |
| 2976 | − lookAtSpot = _myCamera.getPosition() + | |
| 3083 | + lookAtSpot = myAvatar->getPosition() + | |
| 2977 | 3084 | myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); |
| 2978 | 3085 | } else { |
| 2979 | 3086 | lookAtSpot = myAvatar->getHead()->getEyePosition() + |
| @@ -3012,6 +3119,16 @@ void Application::updateThreads(float deltaTime) { · 32 unmodified lines | ||
| 3012 | 3119 | } |
| 3013 | 3120 | } |
| 3014 | 3121 | |
| 3122 | +void Application::toggleOverlays() { | |
| 3123 | + auto newOverlaysVisible = !_overlayConductor.getEnabled(); | |
| 3124 | + Menu::getInstance()->setIsOptionChecked(MenuOption::Overlays, newOverlaysVisible); | |
| 3125 | + _overlayConductor.setEnabled(newOverlaysVisible); | |
| 3126 | +} | |
| 3127 | + | |
| 3128 | +void Application::setOverlaysVisible(bool visible) { | |
| 3129 | + _overlayConductor.setEnabled(visible); | |
| 3130 | +} | |
| 3131 | + | |
| 3015 | 3132 | void Application::cycleCamera() { |
| 3016 | 3133 | auto menu = Menu::getInstance(); |
| 3017 | 3134 | if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { |
| @@ -3070,7 +3187,7 @@ void Application::reloadResourceCaches() { · 52 unmodified lines | ||
| 3070 | 3187 | _viewFrustum.setOrientation(glm::quat()); |
| 3071 | 3188 | queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions); |
| 3072 | 3189 | |
| 3073 | − emptyLocalCache(); | |
| 3190 | + DependencyManager::get<AssetClient>()->clearCache(); | |
| 3074 | 3191 | |
| 3075 | 3192 | DependencyManager::get<AnimationCache>()->refreshAll(); |
| 3076 | 3193 | DependencyManager::get<ModelCache>()->refreshAll(); |
| @@ -3113,11 +3230,33 @@ void Application::updateDialogs(float deltaTime) { · 36 unmodified lines | ||
| 3113 | 3230 | } |
| 3114 | 3231 | |
| 3115 | 3232 | void Application::update(float deltaTime) { |
| 3233 | + | |
| 3234 | + PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1); | |
| 3235 | + | |
| 3116 | 3236 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); |
| 3117 | 3237 | PerformanceWarning warn(showWarnings, "Application::update()"); |
| 3118 | 3238 | |
| 3119 | 3239 | updateLOD(); |
| 3120 | 3240 | |
| 3241 | + if (!_physicsEnabled && _processOctreeStatsCounter > 0) { | |
| 3242 | + | |
| 3243 | + // process octree stats packets are sent in between full sends of a scene. | |
| 3244 | + // We keep physics disabled until we've recieved a full scene and everything near the avatar in that | |
| 3245 | + // scene is ready to compute its collision shape. | |
| 3246 | + | |
| 3247 | + if (nearbyEntitiesAreReadyForPhysics()) { | |
| 3248 | + _physicsEnabled = true; | |
| 3249 | + getMyAvatar()->updateMotionBehaviorFromMenu(); | |
| 3250 | + } else { | |
| 3251 | + auto characterController = getMyAvatar()->getCharacterController(); | |
| 3252 | + if (characterController) { | |
| 3253 | + // if we have a character controller, disable it here so the avatar doesn't get stuck due to | |
| 3254 | + // a non-loading collision hull. | |
| 3255 | + characterController->setEnabled(false); | |
| 3256 | + } | |
| 3257 | + } | |
| 3258 | + } | |
| 3259 | + | |
| 3121 | 3260 | { |
| 3122 | 3261 | PerformanceTimer perfTimer("devices"); |
| 3123 | 3262 | DeviceTracker::updateAll(); |
| @@ -3155,7 +3294,6 @@ void Application::update(float deltaTime) { · 31 unmodified lines | ||
| 3155 | 3294 | |
| 3156 | 3295 | auto myAvatar = getMyAvatar(); |
| 3157 | 3296 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 3158 | − userInputMapper->update(deltaTime); | |
| 3159 | 3297 | |
| 3160 | 3298 | controller::InputCalibrationData calibrationData = { |
| 3161 | 3299 | myAvatar->getSensorToWorldMatrix(), |
| @@ -3163,9 +3301,12 @@ void Application::update(float deltaTime) { · 1 unmodified line | ||
| 3163 | 3301 | myAvatar->getHMDSensorMatrix() |
| 3164 | 3302 | }; |
| 3165 | 3303 | |
| 3304 | + InputPluginPointer keyboardMousePlugin; | |
| 3166 | 3305 | bool jointsCaptured = false; |
| 3167 | 3306 | for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { |
| 3168 | − if (inputPlugin->isActive()) { | |
| 3307 | + if (inputPlugin->getName() == KeyboardMouseDevice::NAME) { | |
| 3308 | + keyboardMousePlugin = inputPlugin; | |
| 3309 | + } else if (inputPlugin->isActive()) { | |
| 3169 | 3310 | inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured); |
| 3170 | 3311 | if (inputPlugin->isJointController()) { |
| 3171 | 3312 | jointsCaptured = true; |
| @@ -3173,6 +3314,12 @@ void Application::update(float deltaTime) { · 1 unmodified line | ||
| 3173 | 3314 | } |
| 3174 | 3315 | } |
| 3175 | 3316 | |
| 3317 | + userInputMapper->update(deltaTime); | |
| 3318 | + | |
| 3319 | + if (keyboardMousePlugin && keyboardMousePlugin->isActive()) { | |
| 3320 | + keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured); | |
| 3321 | + } | |
| 3322 | + | |
| 3176 | 3323 | _controllerScriptingInterface->updateInputControllers(); |
| 3177 | 3324 | |
| 3178 | 3325 | // Transfer the user inputs to the driveKeys |
| @@ -3192,21 +3339,26 @@ void Application::update(float deltaTime) { · 13 unmodified lines | ||
| 3192 | 3339 | myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); |
| 3193 | 3340 | } |
| 3194 | 3341 | |
| 3195 | − controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND); | |
| 3196 | − controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); | |
| 3197 | − Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand(); | |
| 3198 | − setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK)); | |
| 3199 | − setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK)); | |
| 3342 | + controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND); | |
| 3343 | + controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); | |
| 3344 | + auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); | |
| 3345 | + auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix()); | |
| 3346 | + auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; | |
| 3347 | + myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix)); | |
| 3348 | + | |
| 3200 | 3349 | updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... |
| 3201 | 3350 | updateDialogs(deltaTime); // update various stats dialogs if present |
| 3202 | 3351 | |
| 3203 | − _avatarUpdate->synchronousProcess(); | |
| 3352 | + QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>(); | |
| 3204 | 3353 | |
| 3205 | 3354 | if (_physicsEnabled) { |
| 3355 | + PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3356 | + | |
| 3206 | 3357 | PerformanceTimer perfTimer("physics"); |
| 3207 | − AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data(); | |
| 3208 | 3358 | |
| 3209 | 3359 | { |
| 3360 | + PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3361 | + | |
| 3210 | 3362 | PerformanceTimer perfTimer("updateStates)"); |
| 3211 | 3363 | static VectorOfMotionStates motionStates; |
| 3212 | 3364 | _entitySimulation.getObjectsToRemoveFromPhysics(motionStates); |
| @@ -3239,15 +3391,18 @@ void Application::update(float deltaTime) { · 26 unmodified lines | ||
| 3239 | 3391 | }); |
| 3240 | 3392 | } |
| 3241 | 3393 | { |
| 3394 | + PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3242 | 3395 | PerformanceTimer perfTimer("stepSimulation"); |
| 3243 | 3396 | getEntities()->getTree()->withWriteLock([&] { |
| 3244 | 3397 | _physicsEngine->stepSimulation(); |
| 3245 | 3398 | }); |
| 3246 | 3399 | } |
| 3247 | 3400 | { |
| 3248 | − PerformanceTimer perfTimer("havestChanges"); | |
| 3401 | + PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3402 | + PerformanceTimer perfTimer("harvestChanges"); | |
| 3249 | 3403 | if (_physicsEngine->hasOutgoingChanges()) { |
| 3250 | 3404 | getEntities()->getTree()->withWriteLock([&] { |
| 3405 | + PerformanceTimer perfTimer("handleOutgoingChanges"); | |
| 3251 | 3406 | const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges(); |
| 3252 | 3407 | _entitySimulation.handleOutgoingChanges(outgoingChanges, Physics::getSessionUUID()); |
| 3253 | 3408 | avatarManager->handleOutgoingChanges(outgoingChanges); |
| @@ -3263,6 +3418,7 @@ void Application::update(float deltaTime) { · 9 unmodified lines | ||
| 3263 | 3418 | // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk |
| 3264 | 3419 | // deadlock.) |
| 3265 | 3420 | _entitySimulation.handleCollisionEvents(collisionEvents); |
| 3421 | + | |
| 3266 | 3422 | // NOTE: the getEntities()->update() call below will wait for lock |
| 3267 | 3423 | // and will simulate entity motion (the EntityTree has been given an EntitySimulation). |
| 3268 | 3424 | getEntities()->update(); // update the models... |
| @@ -3273,7 +3429,27 @@ void Application::update(float deltaTime) { · 4 unmodified lines | ||
| 3273 | 3429 | } |
| 3274 | 3430 | } |
| 3275 | 3431 | |
| 3432 | + // AvatarManager update | |
| 3433 | + { | |
| 3434 | + PerformanceTimer perfTimer("AvatarManger"); | |
| 3435 | + | |
| 3436 | + qApp->setAvatarSimrateSample(1.0f / deltaTime); | |
| 3437 | + | |
| 3438 | + { | |
| 3439 | + PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3440 | + avatarManager->updateOtherAvatars(deltaTime); | |
| 3441 | + } | |
| 3442 | + | |
| 3443 | + qApp->updateMyAvatarLookAtPosition(); | |
| 3444 | + | |
| 3445 | + { | |
| 3446 | + PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3447 | + avatarManager->updateMyAvatar(deltaTime); | |
| 3448 | + } | |
| 3449 | + } | |
| 3450 | + | |
| 3276 | 3451 | { |
| 3452 | + PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3277 | 3453 | PerformanceTimer perfTimer("overlays"); |
| 3278 | 3454 | _overlays.update(deltaTime); |
| 3279 | 3455 | } |
| @@ -3293,6 +3469,7 @@ void Application::update(float deltaTime) { · 13 unmodified lines | ||
| 3293 | 3469 | |
| 3294 | 3470 | // Update my voxel servers with my current voxel query... |
| 3295 | 3471 | { |
| 3472 | + PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); | |
| 3296 | 3473 | PerformanceTimer perfTimer("queryOctree"); |
| 3297 | 3474 | quint64 sinceLastQuery = now - _lastQueriedTime; |
| 3298 | 3475 | const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; |
| @@ -3330,8 +3507,15 @@ void Application::update(float deltaTime) { · 31 unmodified lines | ||
| 3330 | 3507 | } |
| 3331 | 3508 | } |
| 3332 | 3509 | |
| 3333 | − // update sensorToWorldMatrix for rendering camera. | |
| 3334 | − myAvatar->updateSensorToWorldMatrix(); | |
| 3510 | + { | |
| 3511 | + PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0); | |
| 3512 | + | |
| 3513 | + std::unique_lock<std::mutex> guard(_preRenderLambdasLock); | |
| 3514 | + for (auto& iter : _preRenderLambdas) { | |
| 3515 | + iter.second(); | |
| 3516 | + } | |
| 3517 | + _preRenderLambdas.clear(); | |
| 3518 | + } | |
| 3335 | 3519 | } |
| 3336 | 3520 | |
| 3337 | 3521 | |
| @@ -3406,7 +3590,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 68 unmodified lines | ||
| 3406 | 3590 | _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); |
| 3407 | 3591 | _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); |
| 3408 | 3592 | _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); |
| 3409 | − _octreeQuery.setKeyholeRadius(_viewFrustum.getKeyholeRadius()); | |
| 3593 | + _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius()); | |
| 3410 | 3594 | auto lodManager = DependencyManager::get<LODManager>(); |
| 3411 | 3595 | _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); |
| 3412 | 3596 | _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); |
| @@ -3442,9 +3626,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 29 unmodified lines | ||
| 3442 | 3626 | rootDetails.y * TREE_SCALE, |
| 3443 | 3627 | rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE), |
| 3444 | 3628 | rootDetails.s * TREE_SCALE); |
| 3445 | − ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds); | |
| 3446 | − | |
| 3447 | − if (serverFrustumLocation != ViewFrustum::OUTSIDE) { | |
| 3629 | + if (_viewFrustum.cubeIntersectsKeyhole(serverBounds)) { | |
| 3448 | 3630 | inViewServers++; |
| 3449 | 3631 | } |
| 3450 | 3632 | } |
| @@ -3510,12 +3692,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 59 unmodified lines | ||
| 3510 | 3692 | rootDetails.s * TREE_SCALE); |
| 3511 | 3693 | |
| 3512 | 3694 | |
| 3513 | − ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds); | |
| 3514 | − if (serverFrustumLocation != ViewFrustum::OUTSIDE) { | |
| 3515 | − inView = true; | |
| 3516 | − } else { | |
| 3517 | − inView = false; | |
| 3518 | − } | |
| 3695 | + inView = _viewFrustum.cubeIntersectsKeyhole(serverBounds); | |
| 3519 | 3696 | } else { |
| 3520 | 3697 | if (wantExtraDebugging) { |
| 3521 | 3698 | qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!"; |
| @@ -3740,19 +3917,19 @@ namespace render { · 218 unmodified lines | ||
| 3740 | 3917 | switch (backgroundMode) { |
| 3741 | 3918 | case model::SunSkyStage::SKY_BOX: { |
| 3742 | 3919 | auto skybox = skyStage->getSkybox(); |
| 3743 | − if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) { | |
| 3920 | + if (skybox) { | |
| 3744 | 3921 | PerformanceTimer perfTimer("skybox"); |
| 3745 | 3922 | skybox->render(batch, *(args->_viewFrustum)); |
| 3746 | 3923 | break; |
| 3747 | 3924 | } |
| 3748 | − // If no skybox texture is available, render the SKY_DOME while it loads | |
| 3749 | 3925 | } |
| 3750 | − // fall through to next case | |
| 3926 | + | |
| 3927 | + // Fall through: if no skybox is available, render the SKY_DOME | |
| 3751 | 3928 | case model::SunSkyStage::SKY_DOME: { |
| 3752 | 3929 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { |
| 3753 | 3930 | PerformanceTimer perfTimer("stars"); |
| 3754 | 3931 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| 3755 | − "Application::payloadRender<BackgroundRenderData>() ... stars..."); | |
| 3932 | + "Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars..."); | |
| 3756 | 3933 | // should be the first rendering pass - w/o depth buffer / lighting |
| 3757 | 3934 | |
| 3758 | 3935 | static const float alpha = 1.0f; |
| @@ -3760,6 +3937,7 @@ namespace render { · 1 unmodified line | ||
| 3760 | 3937 | } |
| 3761 | 3938 | } |
| 3762 | 3939 | break; |
| 3940 | + | |
| 3763 | 3941 | case model::SunSkyStage::NO_BACKGROUND: |
| 3764 | 3942 | default: |
| 3765 | 3943 | // this line intentionally left blank |
| @@ -3774,9 +3952,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 8 unmodified lines | ||
| 3774 | 3952 | // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering. |
| 3775 | 3953 | // Then we can move this logic into the Avatar::simulate call. |
| 3776 | 3954 | auto myAvatar = getMyAvatar(); |
| 3777 | − myAvatar->startRender(); | |
| 3778 | 3955 | myAvatar->preRender(renderArgs); |
| 3779 | − myAvatar->endRender(); | |
| 3780 | 3956 | |
| 3781 | 3957 | // Update animation debug draw renderer |
| 3782 | 3958 | AnimDebugDraw::getInstance().update(); |
| @@ -3794,8 +3970,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 11 unmodified lines | ||
| 3794 | 3970 | // The pending changes collecting the changes here |
| 3795 | 3971 | render::PendingChanges pendingChanges; |
| 3796 | 3972 | |
| 3973 | + // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities | |
| 3797 | 3974 | // Background rendering decision |
| 3798 | − if (BackgroundRenderData::_item == 0) { | |
| 3975 | + if (!render::Item::isValidID(BackgroundRenderData::_item)) { | |
| 3799 | 3976 | auto backgroundRenderData = make_shared<BackgroundRenderData>(); |
| 3800 | 3977 | auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData); |
| 3801 | 3978 | BackgroundRenderData::_item = _main3DScene->allocateID(); |
| @@ -3820,8 +3997,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 18 unmodified lines | ||
| 3820 | 3997 | } |
| 3821 | 3998 | } |
| 3822 | 3999 | |
| 4000 | + // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities | |
| 3823 | 4001 | // Make sure the WorldBox is in the scene |
| 3824 | − if (WorldBoxRenderData::_item == 0) { | |
| 4002 | + if (!render::Item::isValidID(WorldBoxRenderData::_item)) { | |
| 3825 | 4003 | auto worldBoxRenderData = make_shared<WorldBoxRenderData>(); |
| 3826 | 4004 | auto worldBoxRenderPayload = make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData); |
| 3827 | 4005 | |
| @@ -3835,18 +4013,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 7 unmodified lines | ||
| 3835 | 4013 | }); |
| 3836 | 4014 | } |
| 3837 | 4015 | |
| 3838 | − // Setup the current Zone Entity lighting and skybox | |
| 3839 | − // Fixme: We need a better soution through an actual render item !!! | |
| 4016 | + // Setup the current Zone Entity lighting | |
| 3840 | 4017 | { |
| 3841 | − DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight()); | |
| 3842 | − auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage(); | |
| 3843 | − DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity()); | |
| 3844 | − | |
| 3845 | − auto skybox = model::SkyboxPointer(); | |
| 3846 | − if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) { | |
| 3847 | − skybox = skyStage->getSkybox(); | |
| 3848 | − } | |
| 3849 | − DependencyManager::get<DeferredLightingEffect>()->setGlobalSkybox(skybox); | |
| 4018 | + auto stage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage(); | |
| 4019 | + DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(stage->getSunLight()); | |
| 3850 | 4020 | } |
| 3851 | 4021 | |
| 3852 | 4022 | { |
| @@ -3864,9 +4034,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 11 unmodified lines | ||
| 3864 | 4034 | _renderEngine->getRenderContext()->args = renderArgs; |
| 3865 | 4035 | |
| 3866 | 4036 | // Before the deferred pass, let's try to use the render engine |
| 3867 | − myAvatar->startRenderRun(); | |
| 3868 | 4037 | _renderEngine->run(); |
| 3869 | − myAvatar->endRenderRun(); | |
| 3870 | 4038 | } |
| 3871 | 4039 | |
| 3872 | 4040 | activeRenderingThread = nullptr; |
| @@ -3928,16 +4096,8 @@ void Application::resetSensors(bool andReload) { · 55 unmodified lines | ||
| 3928 | 4096 | DependencyManager::get<Faceshift>()->reset(); |
| 3929 | 4097 | DependencyManager::get<DdeFaceTracker>()->reset(); |
| 3930 | 4098 | DependencyManager::get<EyeTracker>()->reset(); |
| 3931 | − | |
| 3932 | 4099 | getActiveDisplayPlugin()->resetSensors(); |
| 3933 | − | |
| 3934 | − QScreen* currentScreen = _window->windowHandle()->screen(); | |
| 3935 | − QWindow* mainWindow = _window->windowHandle(); | |
| 3936 | − QPoint windowCenter = mainWindow->geometry().center(); | |
| 3937 | − _glWidget->cursor().setPos(currentScreen, windowCenter); | |
| 3938 | − | |
| 3939 | 4100 | getMyAvatar()->reset(andReload); |
| 3940 | − | |
| 3941 | 4101 | QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "reset", Qt::QueuedConnection); |
| 3942 | 4102 | } |
| 3943 | 4103 | |
| @@ -3990,46 +4150,40 @@ void Application::clearDomainOctreeDetails() { · 46 unmodified lines | ||
| 3990 | 4150 | void Application::domainChanged(const QString& domainHostname) { |
| 3991 | 4151 | updateWindowTitle(); |
| 3992 | 4152 | clearDomainOctreeDetails(); |
| 3993 | − _domainConnectionRefusals.clear(); | |
| 3994 | 4153 | // disable physics until we have enough information about our new location to not cause craziness. |
| 3995 | 4154 | _physicsEnabled = false; |
| 3996 | 4155 | } |
| 3997 | 4156 | |
| 3998 | −void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) { | |
| 3999 | − // Read deny reason from packet | |
| 4000 | − quint16 reasonSize; | |
| 4001 | − message->readPrimitive(&reasonSize); | |
| 4002 | − QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize)); | |
| 4003 | − | |
| 4004 | − // output to the log so the user knows they got a denied connection request | |
| 4005 | − // and check and signal for an access token so that we can make sure they are logged in | |
| 4006 | − qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason; | |
| 4007 | − qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature."; | |
| 4008 | − | |
| 4009 | − if (!_domainConnectionRefusals.contains(reason)) { | |
| 4010 | − _domainConnectionRefusals.append(reason); | |
| 4011 | − emit domainConnectionRefused(reason); | |
| 4012 | − } | |
| 4013 | − | |
| 4014 | − AccountManager::getInstance().checkAndSignalForAccessToken(); | |
| 4015 | −} | |
| 4016 | − | |
| 4017 | −void Application::connectedToDomain(const QString& hostname) { | |
| 4018 | − AccountManager& accountManager = AccountManager::getInstance(); | |
| 4019 | − const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID(); | |
| 4020 | 4157 | |
| 4021 | − if (accountManager.isLoggedIn() && !domainID.isNull()) { | |
| 4022 | − _notifiedPacketVersionMismatchThisDomain = false; | |
| 4023 | − } | |
| 4158 | +void Application::resettingDomain() { | |
| 4159 | + _notifiedPacketVersionMismatchThisDomain = false; | |
| 4024 | 4160 | } |
| 4025 | 4161 | |
| 4026 | 4162 | void Application::nodeAdded(SharedNodePointer node) { |
| 4027 | 4163 | if (node->getType() == NodeType::AvatarMixer) { |
| 4028 | 4164 | // new avatar mixer, send off our identity packet right away |
| 4029 | 4165 | getMyAvatar()->sendIdentityPacket(); |
| 4030 | − } else if (node->getType() == NodeType::AssetServer) { | |
| 4031 | − // the addition of an asset-server always re-enables the upload to asset server menu option | |
| 4032 | − Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(true); | |
| 4166 | + } | |
| 4167 | +} | |
| 4168 | + | |
| 4169 | +void Application::nodeActivated(SharedNodePointer node) { | |
| 4170 | + if (node->getType() == NodeType::AssetServer) { | |
| 4171 | + // asset server just connected - check if we have the asset browser showing | |
| 4172 | + | |
| 4173 | + auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 4174 | + auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer"); | |
| 4175 | + | |
| 4176 | + if (assetDialog) { | |
| 4177 | + auto nodeList = DependencyManager::get<NodeList>(); | |
| 4178 | + | |
| 4179 | + if (nodeList->getThisNodeCanRez()) { | |
| 4180 | + // call reload on the shown asset browser dialog to get the mappings (if permissions allow) | |
| 4181 | + QMetaObject::invokeMethod(assetDialog, "reload"); | |
| 4182 | + } else { | |
| 4183 | + // we switched to an Asset Server that we can't modify, hide the Asset Browser | |
| 4184 | + assetDialog->setVisible(false); | |
| 4185 | + } | |
| 4186 | + } | |
| 4033 | 4187 | } |
| 4034 | 4188 | } |
| 4035 | 4189 | |
| @@ -4045,10 +4199,7 @@ void Application::nodeKilled(SharedNodePointer node) { · 9 unmodified lines | ||
| 4045 | 4199 | |
| 4046 | 4200 | if (node->getType() == NodeType::AudioMixer) { |
| 4047 | 4201 | QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled"); |
| 4048 | − } | |
| 4049 | − | |
| 4050 | − if (node->getType() == NodeType::EntityServer) { | |
| 4051 | − | |
| 4202 | + } else if (node->getType() == NodeType::EntityServer) { | |
| 4052 | 4203 | QUuid nodeUUID = node->getUUID(); |
| 4053 | 4204 | // see if this is the first we've heard of this node... |
| 4054 | 4205 | _entityServerJurisdictions.withReadLock([&] { |
| @@ -4079,10 +4230,16 @@ void Application::nodeKilled(SharedNodePointer node) { · 24 unmodified lines | ||
| 4079 | 4230 | } else if (node->getType() == NodeType::AvatarMixer) { |
| 4080 | 4231 | // our avatar mixer has gone away - clear the hash of avatars |
| 4081 | 4232 | DependencyManager::get<AvatarManager>()->clearOtherAvatars(); |
| 4082 | − } else if (node->getType() == NodeType::AssetServer | |
| 4083 | − && !DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AssetServer)) { | |
| 4084 | − // this was our last asset server - disable the menu option to upload an asset | |
| 4085 | − Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(false); | |
| 4233 | + } else if (node->getType() == NodeType::AssetServer) { | |
| 4234 | + // asset server going away - check if we have the asset browser showing | |
| 4235 | + | |
| 4236 | + auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 4237 | + auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer"); | |
| 4238 | + | |
| 4239 | + if (assetDialog) { | |
| 4240 | + // call reload on the shown asset browser dialog | |
| 4241 | + QMetaObject::invokeMethod(assetDialog, "clear"); | |
| 4242 | + } | |
| 4086 | 4243 | } |
| 4087 | 4244 | } |
| 4088 | 4245 | void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) { |
| @@ -4173,22 +4330,7 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer · 84 unmodified lines | ||
| 4173 | 4330 | }); |
| 4174 | 4331 | }); |
| 4175 | 4332 | |
| 4176 | − if (!_physicsEnabled) { | |
| 4177 | − if (nearbyEntitiesAreReadyForPhysics()) { | |
| 4178 | − // These stats packets are sent in between full sends of a scene. | |
| 4179 | − // We keep physics disabled until we've recieved a full scene and everything near the avatar in that | |
| 4180 | − // scene is ready to compute its collision shape. | |
| 4181 | − _physicsEnabled = true; | |
| 4182 | − getMyAvatar()->updateMotionBehaviorFromMenu(); | |
| 4183 | − } else { | |
| 4184 | − auto characterController = getMyAvatar()->getCharacterController(); | |
| 4185 | − if (characterController) { | |
| 4186 | − // if we have a character controller, disable it here so the avatar doesn't get stuck due to | |
| 4187 | − // a non-loading collision hull. | |
| 4188 | − characterController->setEnabled(false); | |
| 4189 | − } | |
| 4190 | − } | |
| 4191 | − } | |
| 4333 | + _processOctreeStatsCounter++; | |
| 4192 | 4334 | |
| 4193 | 4335 | return statsMessageLength; |
| 4194 | 4336 | } |
| @@ -4270,7 +4412,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri · 75 unmodified lines | ||
| 4270 | 4412 | scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get()); |
| 4271 | 4413 | |
| 4272 | 4414 | scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data()); |
| 4273 | − scriptEngine->registerGlobalObject("Reticle", _compositor.getReticleInterface()); | |
| 4415 | + scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface()); | |
| 4274 | 4416 | } |
| 4275 | 4417 | |
| 4276 | 4418 | bool Application::canAcceptURL(const QString& urlString) const { |
| @@ -4308,7 +4450,10 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) { · 31 unmodified lines | ||
| 4308 | 4450 | } |
| 4309 | 4451 | } |
| 4310 | 4452 | |
| 4311 | − return defaultUpload && askToUploadAsset(urlString); | |
| 4453 | + if (defaultUpload) { | |
| 4454 | + toggleAssetServerWidget(urlString); | |
| 4455 | + } | |
| 4456 | + return defaultUpload; | |
| 4312 | 4457 | } |
| 4313 | 4458 | |
| 4314 | 4459 | void Application::setSessionUUID(const QUuid& sessionUUID) { |
| @@ -4336,8 +4481,8 @@ bool Application::askToSetAvatarUrl(const QString& url) { · 21 unmodified lines | ||
| 4336 | 4481 | |
| 4337 | 4482 | case FSTReader::HEAD_AND_BODY_MODEL: |
| 4338 | 4483 | ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar", |
| 4339 | − "Would you like to use '" + modelName + "' for your avatar?", | |
| 4340 | − QMessageBox::Ok | QMessageBox::Cancel); | |
| 4484 | + "Would you like to use '" + modelName + "' for your avatar?", | |
| 4485 | + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok); | |
| 4341 | 4486 | break; |
| 4342 | 4487 | |
| 4343 | 4488 | default: |
| @@ -4370,79 +4515,6 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { · 26 unmodified lines | ||
| 4370 | 4515 | return true; |
| 4371 | 4516 | } |
| 4372 | 4517 | |
| 4373 | −bool Application::askToUploadAsset(const QString& filename) { | |
| 4374 | − if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) { | |
| 4375 | − OffscreenUi::warning(_window, "Failed Upload", | |
| 4376 | − QString("You don't have upload rights on that domain.\n\n")); | |
| 4377 | − return false; | |
| 4378 | − } | |
| 4379 | − | |
| 4380 | − QUrl url { filename }; | |
| 4381 | − if (auto upload = DependencyManager::get<AssetClient>()->createUpload(url.toLocalFile())) { | |
| 4382 | − | |
| 4383 | − QMessageBox messageBox; | |
| 4384 | − messageBox.setWindowTitle("Asset upload"); | |
| 4385 | − messageBox.setText("You are about to upload the following file to the asset server:\n" + | |
| 4386 | − url.toDisplayString()); | |
| 4387 | − messageBox.setInformativeText("Do you want to continue?"); | |
| 4388 | − messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); | |
| 4389 | − messageBox.setDefaultButton(QMessageBox::Ok); | |
| 4390 | − | |
| 4391 | − // Option to drop model in world for models | |
| 4392 | − if (filename.endsWith(FBX_EXTENSION, Qt::CaseInsensitive) || filename.endsWith(OBJ_EXTENSION, Qt::CaseInsensitive)) { | |
| 4393 | − auto checkBox = new QCheckBox(&messageBox); | |
| 4394 | − checkBox->setText("Add to scene"); | |
| 4395 | − messageBox.setCheckBox(checkBox); | |
| 4396 | − } | |
| 4397 | − | |
| 4398 | − if (messageBox.exec() != QMessageBox::Ok) { | |
| 4399 | − upload->deleteLater(); | |
| 4400 | − return false; | |
| 4401 | − } | |
| 4402 | − | |
| 4403 | − // connect to the finished signal so we know when the AssetUpload is done | |
| 4404 | − if (messageBox.checkBox() && (messageBox.checkBox()->checkState() == Qt::Checked)) { | |
| 4405 | − // Custom behavior for models | |
| 4406 | − QObject::connect(upload, &AssetUpload::finished, this, &Application::modelUploadFinished); | |
| 4407 | − } else { | |
| 4408 | − QObject::connect(upload, &AssetUpload::finished, | |
| 4409 | − &AssetUploadDialogFactory::getInstance(), | |
| 4410 | − &AssetUploadDialogFactory::handleUploadFinished); | |
| 4411 | − } | |
| 4412 | − | |
| 4413 | − // start the upload now | |
| 4414 | − upload->start(); | |
| 4415 | − return true; | |
| 4416 | − } | |
| 4417 | − | |
| 4418 | − // display a message box with the error | |
| 4419 | − OffscreenUi::warning(_window, "Failed Upload", QString("Failed to upload %1.\n\n").arg(filename)); | |
| 4420 | − return false; | |
| 4421 | −} | |
| 4422 | − | |
| 4423 | −void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) { | |
| 4424 | − auto filename = QFileInfo(upload->getFilename()).fileName(); | |
| 4425 | − | |
| 4426 | − if ((upload->getError() == AssetUpload::NoError) && | |
| 4427 | − (FBX_EXTENSION.endsWith(upload->getExtension(), Qt::CaseInsensitive) || | |
| 4428 | − OBJ_EXTENSION.endsWith(upload->getExtension(), Qt::CaseInsensitive))) { | |
| 4429 | − | |
| 4430 | − auto entities = DependencyManager::get<EntityScriptingInterface>(); | |
| 4431 | − | |
| 4432 | − EntityItemProperties properties; | |
| 4433 | − properties.setType(EntityTypes::Model); | |
| 4434 | − properties.setModelURL(QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension())); | |
| 4435 | − properties.setPosition(_myCamera.getPosition() + _myCamera.getOrientation() * Vectors::FRONT * 2.0f); | |
| 4436 | − properties.setName(QUrl(upload->getFilename()).fileName()); | |
| 4437 | − | |
| 4438 | − entities->addEntity(properties); | |
| 4439 | − | |
| 4440 | − upload->deleteLater(); | |
| 4441 | − } else { | |
| 4442 | − AssetUploadDialogFactory::getInstance().handleUploadFinished(upload, hash); | |
| 4443 | − } | |
| 4444 | −} | |
| 4445 | − | |
| 4446 | 4518 | bool Application::askToWearAvatarAttachmentUrl(const QString& url) { |
| 4447 | 4519 | |
| 4448 | 4520 | QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); |
| @@ -4542,6 +4614,22 @@ void Application::toggleRunningScriptsWidget() { · 93 unmodified lines | ||
| 4542 | 4614 | //} |
| 4543 | 4615 | } |
| 4544 | 4616 | |
| 4617 | +void Application::toggleAssetServerWidget(QString filePath) { | |
| 4618 | + if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) { | |
| 4619 | + return; | |
| 4620 | + } | |
| 4621 | + | |
| 4622 | + static const QUrl url { "AssetServer.qml" }; | |
| 4623 | + | |
| 4624 | + auto startUpload = [=](QQmlContext* context, QObject* newObject){ | |
| 4625 | + if (!filePath.isEmpty()) { | |
| 4626 | + emit uploadRequest(filePath); | |
| 4627 | + } | |
| 4628 | + }; | |
| 4629 | + DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload); | |
| 4630 | + startUpload(nullptr, nullptr); | |
| 4631 | +} | |
| 4632 | + | |
| 4545 | 4633 | void Application::packageModel() { |
| 4546 | 4634 | ModelPackager::package(); |
| 4547 | 4635 | } |
| @@ -4557,33 +4645,6 @@ void Application::openUrl(const QUrl& url) { · 9 unmodified lines | ||
| 4557 | 4645 | } |
| 4558 | 4646 | } |
| 4559 | 4647 | |
| 4560 | −void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) { | |
| 4561 | − // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed | |
| 4562 | − const QString VOXEL_SETTINGS_KEY = "voxels"; | |
| 4563 | − const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; | |
| 4564 | − const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; | |
| 4565 | − const QString VOXEL_WALLET_UUID = "voxel-wallet"; | |
| 4566 | − | |
| 4567 | − const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject(); | |
| 4568 | − | |
| 4569 | − qint64 satoshisPerVoxel = 0; | |
| 4570 | − qint64 satoshisPerMeterCubed = 0; | |
| 4571 | − QUuid voxelWalletUUID; | |
| 4572 | − | |
| 4573 | − if (!domainSettingsObject.isEmpty()) { | |
| 4574 | − float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble(); | |
| 4575 | − float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble(); | |
| 4576 | − | |
| 4577 | − satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); | |
| 4578 | − satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); | |
| 4579 | − | |
| 4580 | − voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString()); | |
| 4581 | − } | |
| 4582 | − | |
| 4583 | − qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed"; | |
| 4584 | − qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID; | |
| 4585 | −} | |
| 4586 | − | |
| 4587 | 4648 | void Application::loadDialog() { |
| 4588 | 4649 | auto scriptEngines = DependencyManager::get<ScriptEngines>(); |
| 4589 | 4650 | QString fileNameString = OffscreenUi::getOpenFileName( |
| @@ -4657,34 +4718,6 @@ float Application::getRenderResolutionScale() const { · 67 unmodified lines | ||
| 4657 | 4718 | } |
| 4658 | 4719 | } |
| 4659 | 4720 | |
| 4660 | −int Application::getRenderAmbientLight() const { | |
| 4661 | − if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLightGlobal)) { | |
| 4662 | − return -1; | |
| 4663 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight0)) { | |
| 4664 | − return 0; | |
| 4665 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight1)) { | |
| 4666 | − return 1; | |
| 4667 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight2)) { | |
| 4668 | − return 2; | |
| 4669 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight3)) { | |
| 4670 | − return 3; | |
| 4671 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight4)) { | |
| 4672 | − return 4; | |
| 4673 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight5)) { | |
| 4674 | − return 5; | |
| 4675 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight6)) { | |
| 4676 | − return 6; | |
| 4677 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight7)) { | |
| 4678 | − return 7; | |
| 4679 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight8)) { | |
| 4680 | − return 8; | |
| 4681 | − } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight9)) { | |
| 4682 | − return 9; | |
| 4683 | − } else { | |
| 4684 | − return -1; | |
| 4685 | − } | |
| 4686 | −} | |
| 4687 | − | |
| 4688 | 4721 | void Application::notifyPacketVersionMismatch() { |
| 4689 | 4722 | if (!_notifiedPacketVersionMismatchThisDomain) { |
| 4690 | 4723 | _notifiedPacketVersionMismatchThisDomain = true; |
| @@ -4697,7 +4730,7 @@ void Application::notifyPacketVersionMismatch() { · 6 unmodified lines | ||
| 4697 | 4730 | } |
| 4698 | 4731 | |
| 4699 | 4732 | void Application::checkSkeleton() { |
| 4700 | − if (getMyAvatar()->getSkeletonModel().isActive() && !getMyAvatar()->getSkeletonModel().hasSkeleton()) { | |
| 4733 | + if (getMyAvatar()->getSkeletonModel()->isActive() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) { | |
| 4701 | 4734 | qCDebug(interfaceapp) << "MyAvatar model has no skeleton"; |
| 4702 | 4735 | |
| 4703 | 4736 | QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; |
| @@ -4786,11 +4819,18 @@ qreal Application::getDevicePixelRatio() { · 82 unmodified lines | ||
| 4786 | 4819 | } |
| 4787 | 4820 | |
| 4788 | 4821 | DisplayPlugin* Application::getActiveDisplayPlugin() { |
| 4789 | − if (nullptr == _displayPlugin) { | |
| 4790 | − updateDisplayMode(); | |
| 4791 | − Q_ASSERT(_displayPlugin); | |
| 4822 | + DisplayPlugin* result = nullptr; | |
| 4823 | + if (QThread::currentThread() == thread()) { | |
| 4824 | + if (nullptr == _displayPlugin) { | |
| 4825 | + updateDisplayMode(); | |
| 4826 | + Q_ASSERT(_displayPlugin); | |
| 4827 | + } | |
| 4828 | + result = _displayPlugin.get(); | |
| 4829 | + } else { | |
| 4830 | + std::unique_lock<std::mutex> lock(_displayPluginLock); | |
| 4831 | + result = _displayPlugin.get(); | |
| 4792 | 4832 | } |
| 4793 | − return _displayPlugin.get(); | |
| 4833 | + return result; | |
| 4794 | 4834 | } |
| 4795 | 4835 | |
| 4796 | 4836 | const DisplayPlugin* Application::getActiveDisplayPlugin() const { |
| @@ -4813,7 +4853,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 16 unmodified lines | ||
| 4813 | 4853 | groupingMenu = "Developer"; |
| 4814 | 4854 | break; |
| 4815 | 4855 | default: |
| 4816 | − groupingMenu = "Standard"; | |
| 4856 | + groupingMenu = "Standard"; | |
| 4817 | 4857 | break; |
| 4818 | 4858 | } |
| 4819 | 4859 | |
| @@ -4826,7 +4866,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 6 unmodified lines | ||
| 4826 | 4866 | auto action = menu->addActionToQMenuAndActionHash(parent, |
| 4827 | 4867 | name, 0, qApp, |
| 4828 | 4868 | SLOT(updateDisplayMode()), |
| 4829 | − QAction::NoRole, UNSPECIFIED_POSITION, groupingMenu); | |
| 4869 | + QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu); | |
| 4830 | 4870 | |
| 4831 | 4871 | action->setCheckable(true); |
| 4832 | 4872 | action->setChecked(active); |
| @@ -4835,6 +4875,19 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 2 unmodified lines | ||
| 4835 | 4875 | } |
| 4836 | 4876 | |
| 4837 | 4877 | void Application::updateDisplayMode() { |
| 4878 | + // Unsafe to call this method from anything but the main thread | |
| 4879 | + if (QThread::currentThread() != thread()) { | |
| 4880 | + qFatal("Attempted to switch display plugins from a non-main thread"); | |
| 4881 | + } | |
| 4882 | + | |
| 4883 | + // Some plugins *cough* Oculus *cough* process message events from inside their | |
| 4884 | + // display function, and we don't want to change the display plugin underneath | |
| 4885 | + // the paintGL call, so we need to guard against that | |
| 4886 | + // The current oculus runtime doesn't do this anymore | |
| 4887 | + if (_inPaint) { | |
| 4888 | + qFatal("Attempted to switch display plugins while in painting"); | |
| 4889 | + } | |
| 4890 | + | |
| 4838 | 4891 | auto menu = Menu::getInstance(); |
| 4839 | 4892 | auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); |
| 4840 | 4893 | |
| @@ -4891,71 +4944,62 @@ void Application::updateDisplayMode() { · 50 unmodified lines | ||
| 4891 | 4944 | } |
| 4892 | 4945 | } |
| 4893 | 4946 | |
| 4894 | − auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 4895 | − DisplayPluginPointer oldDisplayPlugin = _displayPlugin; | |
| 4896 | − if (newDisplayPlugin == oldDisplayPlugin) { | |
| 4947 | + if (newDisplayPlugin == _displayPlugin) { | |
| 4897 | 4948 | return; |
| 4898 | 4949 | } |
| 4899 | 4950 | |
| 4900 | − // Some plugins *cough* Oculus *cough* process message events from inside their | |
| 4901 | − // display function, and we don't want to change the display plugin underneath | |
| 4902 | − // the paintGL call, so we need to guard against that | |
| 4903 | − if (_inPaint) { | |
| 4904 | − qDebug() << "Deferring plugin switch until out of painting"; | |
| 4905 | − // Have the old plugin stop requesting renders | |
| 4906 | − oldDisplayPlugin->stop(); | |
| 4907 | − QTimer* timer = new QTimer(); | |
| 4908 | − timer->singleShot(500, [this, timer] { | |
| 4909 | − timer->deleteLater(); | |
| 4910 | − updateDisplayMode(); | |
| 4911 | − }); | |
| 4912 | − return; | |
| 4913 | − } | |
| 4951 | + auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 4914 | 4952 | |
| 4953 | + // Make the switch atomic from the perspective of other threads | |
| 4954 | + { | |
| 4955 | + std::unique_lock<std::mutex> lock(_displayPluginLock); | |
| 4915 | 4956 | |
| 4916 | − if (!_pluginContainer->currentDisplayActions().isEmpty()) { | |
| 4917 | − auto menu = Menu::getInstance(); | |
| 4918 | − foreach(auto itemInfo, _pluginContainer->currentDisplayActions()) { | |
| 4919 | − menu->removeMenuItem(itemInfo.first, itemInfo.second); | |
| 4957 | + auto oldDisplayPlugin = _displayPlugin; | |
| 4958 | + if (_displayPlugin) { | |
| 4959 | + _displayPlugin->deactivate(); | |
| 4920 | 4960 | } |
| 4921 | − _pluginContainer->currentDisplayActions().clear(); | |
| 4922 | − } | |
| 4923 | 4961 | |
| 4924 | − if (newDisplayPlugin) { | |
| 4925 | − _offscreenContext->makeCurrent(); | |
| 4926 | − newDisplayPlugin->activate(); | |
| 4962 | + // FIXME probably excessive and useless context switching | |
| 4927 | 4963 | _offscreenContext->makeCurrent(); |
| 4928 | − offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); | |
| 4929 | − _offscreenContext->makeCurrent(); | |
| 4930 | − } | |
| 4931 | 4964 | |
| 4932 | − oldDisplayPlugin = _displayPlugin; | |
| 4933 | − _displayPlugin = newDisplayPlugin; | |
| 4965 | + bool active = newDisplayPlugin->activate(); | |
| 4934 | 4966 | |
| 4935 | − // If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed | |
| 4936 | − // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1 | |
| 4937 | − bool newPluginWantsHMDTools = newDisplayPlugin ? | |
| 4938 | − (newDisplayPlugin->isHmd() && (newDisplayPlugin->getHmdScreen() >= 0)) : false; | |
| 4939 | − bool oldPluginWantedHMDTools = oldDisplayPlugin ? | |
| 4940 | − (oldDisplayPlugin->isHmd() && (oldDisplayPlugin->getHmdScreen() >= 0)) : false; | |
| 4967 | + if (!active) { | |
| 4968 | + // If the new plugin fails to activate, fallback to last display | |
| 4969 | + qWarning() << "Failed to activate display: " << newDisplayPlugin->getName(); | |
| 4970 | + newDisplayPlugin = oldDisplayPlugin; | |
| 4941 | 4971 | |
| 4942 | − // Only show the hmd tools after the correct plugin has | |
| 4943 | − // been activated so that it's UI is setup correctly | |
| 4944 | − if (newPluginWantsHMDTools) { | |
| 4945 | − _pluginContainer->showDisplayPluginsTools(); | |
| 4946 | − } | |
| 4972 | + if (newDisplayPlugin) { | |
| 4973 | + qWarning() << "Falling back to last display: " << newDisplayPlugin->getName(); | |
| 4974 | + active = newDisplayPlugin->activate(); | |
| 4975 | + } | |
| 4947 | 4976 | |
| 4948 | − if (oldDisplayPlugin) { | |
| 4949 | − oldDisplayPlugin->deactivate(); | |
| 4950 | − _offscreenContext->makeCurrent(); | |
| 4977 | + // If there is no last display, or | |
| 4978 | + // If the last display fails to activate, fallback to desktop | |
| 4979 | + if (!active) { | |
| 4980 | + newDisplayPlugin = displayPlugins.at(0); | |
| 4981 | + qWarning() << "Falling back to display: " << newDisplayPlugin->getName(); | |
| 4982 | + active = newDisplayPlugin->activate(); | |
| 4983 | + } | |
| 4951 | 4984 | |
| 4952 | − // if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools | |
| 4953 | − if (oldPluginWantedHMDTools && !newPluginWantsHMDTools) { | |
| 4954 | − DependencyManager::get<DialogsManager>()->hmdTools(false); | |
| 4985 | + if (!active) { | |
| 4986 | + qFatal("Failed to activate fallback plugin"); | |
| 4987 | + } | |
| 4955 | 4988 | } |
| 4989 | + | |
| 4990 | + _offscreenContext->makeCurrent(); | |
| 4991 | + offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); | |
| 4992 | + _offscreenContext->makeCurrent(); | |
| 4993 | + getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); | |
| 4994 | + _displayPlugin = newDisplayPlugin; | |
| 4956 | 4995 | } |
| 4996 | + | |
| 4997 | + | |
| 4957 | 4998 | emit activeDisplayPluginChanged(); |
| 4958 | − resetSensors(); | |
| 4999 | + | |
| 5000 | + // reset the avatar, to set head and hand palms back to a resonable default pose. | |
| 5001 | + getMyAvatar()->reset(false); | |
| 5002 | + | |
| 4959 | 5003 | Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); |
| 4960 | 5004 | } |
| 4961 | 5005 | |
| @@ -5049,70 +5093,11 @@ mat4 Application::getEyeOffset(int eye) const { · 87 unmodified lines | ||
| 5049 | 5093 | |
| 5050 | 5094 | mat4 Application::getHMDSensorPose() const { |
| 5051 | 5095 | if (isHMDMode()) { |
| 5052 | − return getActiveDisplayPlugin()->getHeadPose(_frameCount); | |
| 5096 | + return getActiveDisplayPlugin()->getHeadPose(); | |
| 5053 | 5097 | } |
| 5054 | 5098 | return mat4(); |
| 5055 | 5099 | } |
| 5056 | 5100 | |
| 5057 | −void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) { | |
| 5058 | − | |
| 5059 | − // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't | |
| 5060 | − // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more | |
| 5061 | − // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about | |
| 5062 | − // controller::Pose. More work is needed to clean this up. | |
| 5063 | − hand->modifyPalm(whichHand, [&](PalmData& palm) { | |
| 5064 | − palm.setActive(pose.isValid()); | |
| 5065 | − | |
| 5066 | − // controller pose is in Avatar frame. | |
| 5067 | − glm::vec3 position = pose.getTranslation(); | |
| 5068 | − glm::quat rotation = pose.getRotation(); | |
| 5069 | − | |
| 5070 | − // Compute current velocity from position change | |
| 5071 | − glm::vec3 rawVelocity; | |
| 5072 | − if (deltaTime > 0.0f) { | |
| 5073 | − rawVelocity = (position - palm.getRawPosition()) / deltaTime; | |
| 5074 | − } else { | |
| 5075 | − rawVelocity = glm::vec3(0.0f); | |
| 5076 | − } | |
| 5077 | − palm.setRawVelocity(rawVelocity); // meters/sec | |
| 5078 | − | |
| 5079 | − // Angular Velocity of Palm | |
| 5080 | − glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation()); | |
| 5081 | − glm::vec3 angularVelocity(0.0f); | |
| 5082 | − float rotationAngle = glm::angle(deltaRotation); | |
| 5083 | − if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { | |
| 5084 | − angularVelocity = glm::normalize(glm::axis(deltaRotation)); | |
| 5085 | − angularVelocity *= (rotationAngle / deltaTime); | |
| 5086 | − palm.setRawAngularVelocity(angularVelocity); | |
| 5087 | − } else { | |
| 5088 | − palm.setRawAngularVelocity(glm::vec3(0.0f)); | |
| 5089 | − } | |
| 5090 | − | |
| 5091 | − if (controller::InputDevice::getLowVelocityFilter()) { | |
| 5092 | − // Use a velocity sensitive filter to damp small motions and preserve large ones with | |
| 5093 | − // no latency. | |
| 5094 | − float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); | |
| 5095 | − position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); | |
| 5096 | − rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter); | |
| 5097 | − } | |
| 5098 | − palm.setRawPosition(position); | |
| 5099 | − palm.setRawRotation(rotation); | |
| 5100 | − | |
| 5101 | − // Store the one fingertip in the palm structure so we can track velocity | |
| 5102 | − const float FINGER_LENGTH = 0.3f; // meters | |
| 5103 | − const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); | |
| 5104 | − const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; | |
| 5105 | − glm::vec3 oldTipPosition = palm.getTipRawPosition(); | |
| 5106 | − if (deltaTime > 0.0f) { | |
| 5107 | − palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); | |
| 5108 | − } else { | |
| 5109 | − palm.setTipVelocity(glm::vec3(0.0f)); | |
| 5110 | − } | |
| 5111 | − palm.setTipPosition(newTipPosition); | |
| 5112 | − palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger | |
| 5113 | − }); | |
| 5114 | −} | |
| 5115 | − | |
| 5116 | 5101 | void Application::crashApplication() { |
| 5117 | 5102 | qCDebug(interfaceapp) << "Intentionally crashed Interface"; |
| 5118 | 5103 | QObject* object = nullptr; |
| @@ -5120,6 +5105,15 @@ void Application::crashApplication() { · 1 unmodified line | ||
| 5120 | 5105 | Q_UNUSED(value); |
| 5121 | 5106 | } |
| 5122 | 5107 | |
| 5108 | +void Application::deadlockApplication() { | |
| 5109 | + qCDebug(interfaceapp) << "Intentionally deadlocked Interface"; | |
| 5110 | + // Using a loop that will *technically* eventually exit (in ~600 billion years) | |
| 5111 | + // to avoid compiler warnings about a loop that will never exit | |
| 5112 | + for (uint64_t i = 1; i != 0; ++i) { | |
| 5113 | + QThread::sleep(1); | |
| 5114 | + } | |
| 5115 | +} | |
| 5116 | + | |
| 5123 | 5117 | void Application::setActiveDisplayPlugin(const QString& pluginName) { |
| 5124 | 5118 | auto menu = Menu::getInstance(); |
| 5125 | 5119 | foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { |
| @@ -5158,3 +5152,13 @@ void Application::readArgumentsFromLocalSocket() { · 32 unmodified lines | ||
| 5158 | 5152 | qApp->openUrl(QString::fromUtf8(message)); |
| 5159 | 5153 | } |
| 5160 | 5154 | } |
| 5155 | + | |
| 5156 | +void Application::showDesktop() { | |
| 5157 | + if (!_overlayConductor.getEnabled()) { | |
| 5158 | + _overlayConductor.setEnabled(true); | |
| 5159 | + } | |
| 5160 | +} | |
| 5161 | + | |
| 5162 | +CompositorHelper& Application::getApplicationCompositor() const { | |
| 5163 | + return *DependencyManager::get<CompositorHelper>(); | |
| 5164 | +} | |